【问题标题】:C# - ThreadPool QueueUserWorkItem Use?C# - ThreadPool QueueUserWorkItem 使用?
【发布时间】:2013-07-02 07:33:39
【问题描述】:

现在我正在使用以下代码添加排队线程。我不喜欢它。我的同事也不会,因为他们不太了解 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<>


编辑:从答案和 cmets 中得到 (b),我已经更喜欢它了。

【问题讨论】:

  • 您的场景中的 ThreadPool.QueueUserWorkItem() 有什么问题?
  • 您可以使用“delegate”关键字。类似于 ThreadPool.QueueUserWorkItem(delegate {doStuff("");})。与上述方法相同,但如您所愿,这只是另一种方法..
  • 为什么你会认为委托语法比 lambdas 更简洁!?
  • @mini-me:那么也许你的程序应该用 C 编写,或者你应该让你的同事学习他们正在编码的语言。说真的,花 20 分钟学习 lambda 会很容易节省他们在第一天就花了那么多时间进行编码(主要是由于LINQ。尝试用 C# 编写 C 代码只会导致更多问题。

标签: c# multithreading delegates threadpool


【解决方案1】:

我不完全确定您要查找哪种语法,但如果您不喜欢示例中未使用的 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!"); });

但这有什么意义呢?它的可读性要差得多!

【讨论】:

  • 其实我想避免 Lambda 的事情。
  • @mini-me 好吧,我添加了一个避免使用 lambda 的示例......这更糟糕,IMO。
  • @mini-me Lambda 表达式在很久以前几乎取代了匿名代表。所以你最好早点习惯它们。
【解决方案2】:

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

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

这就是你如何使用 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#

【讨论】:

  • 是否也可以使用FuncAction 或没有额外的调用主体?
  • 这将是可能的,当您构建 WaitCallback 时,不要发送简单的“委托”,而是发送您自己的委托。 但是,您希望如何返回结果?通常在向工作线程发送操作时,您不会期望得到结果,只是为了确保工作完成。您可以通过实现异步模式来实现这一点。 c-sharpcorner.com/UploadFile/rmcochran/…
【解决方案3】:

这个怎么样?

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");
    }
}

【讨论】:

  • 如果不是那样的签名限制,这对我来说将是一个胜利者。但是给它一个方法并完成它仍然有点酷。
  • @Bitterblue 我添加了第二种方法。前面需要做更多的工作,但我认为最容易使用。
【解决方案4】:

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

看看我的扩展方法

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);
   }
}

【讨论】:

    猜你喜欢
    • 2011-07-10
    • 2019-02-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-24
    • 2012-02-05
    • 1970-01-01
    • 2011-07-25
    相关资源
    最近更新 更多