30

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


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

4

4 回答 4

20

我不完全确定您正在寻找哪种语法,但是如果您不喜欢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!"); });

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

于 2013-07-02T07:48:12.833 回答
19

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

但是,您可以为接收参数、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
.NET 中的通用线程池
delegate.BeginInvoke 和在 C# 中使用 ThreadPool 线程之间的区别

于 2013-07-02T07:49:47.863 回答
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");
    }
}
于 2017-04-29T14:26:33.700 回答
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);
   }
}
于 2020-09-09T13:12:42.040 回答