C# - ThreadPool QueueUserWorkItem使用?

时间:2013-07-02 07:33:39

标签: c# multithreading delegates threadpool

就在我现在使用以下代码添加排队的线程。我不喜欢它。而且我的同事也不会因为他们不太了解C#。我想要的当然是将一个方法排队在一个新线程中执行。

private static void doStuff(string parameter)
{
    // does stuff
}

// call (a)
ThreadPool.QueueUserWorkItem(a => doStuff("hello world"));
// call (b)
ThreadPool.QueueUserWorkItem(delegate { doStuff("hello world"); });

ThreadPool.QueueUserWorkItem还有其他用途变体吗?

最好是另一个1-Line-Call。如果可能,请使用Func<>Action<>

<小时/> 编辑:从答案和评论中得到(b),我已经更喜欢它了。

4 个答案:

答案 0 :(得分:17)

您的问题的答案取决于您如何设计应用程序。你把它放在一个共同的项目中吗?你不想开销简单的操作。

但是,你可以为ThreadPool QueueUserItem创建一个接收params,1 param,2 param等的通用调用。这很好,而不是发送一个简单的字符串并受到限制。

这是如何使用WaitCallback实现QueueUserItem的参数:

ThreadPool.QueueUserWorkItem(
  new WaitCallback(delegate(object state)
  { YourMethod(Param1, Param2, Param3); }), null);

取自C# Execute Method (with Parameters) with ThreadPool

一些想法的链接:
http://msdn.microsoft.com/en-us/library/4yd16hza.aspx
Generic ThreadPool in .NET
Difference between delegate.BeginInvoke and using ThreadPool threads in C#

答案 1 :(得分:16)

我不完全确定您正在寻找什么样的语法,但如果您不喜欢示例中未使用的a,为什么不使用Task

Task.Run(() => doStuff("hello world"));

它看起来并没有好多少,但至少它没有未使用的标识符。

注意:Task.Run()是.Net 4.5或更高版本。如果你正在使用.Net 4,你必须这样做:

Task.Factory.StartNew(() => doStuff("hello world"));

并不短。

以上两者都使用线程池。

如果你真的必须避免使用lambda,你可以使用匿名委托(@nowhewhomustnotbenamed已经提到过):

Task.Run(delegate { doStuff("Hello, World!"); });

但那有什么意义呢?它的可读性低得多!

答案 2 :(得分:2)

这个怎么样?

class Program
{
    static void Main(string[] args)
    {
        ThreadPool.QueueUserWorkItem(MyWork, "text");
        Console.ReadKey();
    }

    private static void MyWork(object argument)
    {
        Console.WriteLine("Argument: " + argument);
    }
}

或者如果你不想成为签名限制并且有一种简单的方法将方法放在一个线程上你就可以这样做。对于那些有和没有返回值并且最多有6个参数的方法,它需要如果我没有弄错的话,你要定义12个重载。它需要更多的工作,但使用起来更简单。

class Program
{
    static void Main(string[] args)
    {
        var myClass = new MyClass();
        myClass.DoWork();
        Console.ReadKey();
    }
}

public static class ObjectThreadExtension
{
    public static void OnThread(this object @object, Action action)
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            action();
        });
    }

    public static void OnThread<T>(this object @object, Action<T> action, T argument)
    {
        ThreadPool.QueueUserWorkItem(state =>
        {
            action(argument);
        });
    }
}

public class MyClass
{
    private void MyMethod()
    {
        Console.WriteLine("I could have been put on a thread if you like.");
    }

    private void MySecondMethod(string argument)
    {
        Console.WriteLine(argument);
    }

    public void DoWork()
    {
        this.OnThread(MyMethod);
        this.OnThread(MySecondMethod, "My argument");
    }
}

答案 3 :(得分:0)

我认为使用框架而不是“反对”框架总是好的。 我并不是说其他​​答案是错误的,它们都是在基本方法周围调用包装器,有时在出现问题时会抑制堆栈。

看看我的扩展方法

public static class SMTPLoggerExtensionMethods
{
    class MailData
    {
        public MailData(EMailRoles role, string subject,string body, bool isHtml)
        {
            this.Roles = role;
            this.Subject = subject;
            this.Body = body;
            this.IsHtml = isHtml;

        }
        public EMailRoles Roles { get;  }
        public string Subject { get;  }
        public string Body { get; }
        public bool IsHtml { get; }
    }
    /// <summary>
    /// Send an email to all users defined in the email configuration of the IFireWall using a non-blocking method
    /// </summary>
    /// <param name="fireWall">The firewall instance</param>
    /// <param name="roles">The roles that are to be send the email</param>
    /// <param name="subject">the subject for the email</param>
    /// <param name="body">the email body</param>
    /// <param name="isHtml">indicating the email is HTML formated </param>
    public static void SendEmail(this IFireWall fireWall, EMailRoles roles, string subject, string body, bool isHtml)
    {
        var state = new MailData(roles, subject, body, isHtml);
        System.Threading.ThreadPool.QueueUserWorkItem(PSendMail, state);
    }

    private static void PSendMail(object? state)
    {
        if (state is MailData mail)
        {
            var smtp = DIContainer.GetDefaultInstanceOrNull<SMTPMailLogger>();
            smtp?.Enqueue(mail.Roles, mail.Subject, mail.Body, mail.IsHtml);
        }
    }
}

不能保证将发送电子邮件,也不会将错误填充回呼叫者,但是作为一种非阻塞方法,可以将电子邮件发送给给定组的所有用户...为什么不这样做。没有错误且响应缓慢,向给定组中的所有用户发送电子邮件可能就适合您的用例。

然后我将调用扩展方法,如下面的控制器示例中所示。

没有阻塞,在我的情况下,如果我们错过一封电子邮件,也不会感到压力,因为日志中包含用户体验良好的错误....只是谁真正在看日志;-)

[BlockDuration(seconds: 60, sliding: true, doubleDurationPerIncident: true)]
public class HomeController : Controller
{
    private readonly IFireWall _fireWall;
    private readonly IPageRequest _page;
    private ILatLongRepository _latLongRepository;

    public HomeController(IFireWall fireWall, IPageRequest page, ILatLongRepository latLongRepository)
    {
        _page = page;
        _fireWall = fireWall;
        _latLongRepository = latLongRepository;
    }


    [GeoIgnore(maxRequest: 5)]
    [Ignore(skip: FireWallGuardModules.RejectPenetrationAttempts
                | FireWallGuardModules.RejectRepeatViolations
                | FireWallGuardModules.RejectWrongUserType
        , skipCount: 5)]
    public IActionResult Blocked(BlockingReason id)
    {

        //call the extension method here
        _fireWall.SendEmail(roles: Walter.Web.FireWall.Destinations.Email.EMailRoles.SecurityRelevant | Walter.Web.FireWall.Destinations.Email.EMailRoles.FireWallAdministrationViolations
            , subject: $"Access to {_page.OriginalUrl} rejected for user in {_latLongRepository.QueryMapLocation(_page.IPAddress)}"
            , body: MailUtils.MakeMailBodyForBlcokedUser(page: _page)
            , isHtml: true
            );

        if (_page.User.AsFirewallUser().UserType.HasFlag(UserTypes.IsMalicious))
        {
            return View("_Malicious", id);
        }
        return View(id);
   }
}
相关问题