C# - ThreadPool QueueUserWorkItem Use?

前端 未结 4 764
星月不相逢
星月不相逢 2020-12-24 12:39

Just right now I\'m using following code to add queued threads. I don\'t like it. And my colleagues won\'t either because they don\'t know C# very well. All I want is of cou

相关标签:
4条回答
  • 2020-12-24 13:03

    The answer for your question depends on how you design the application. Do you put it inside a common project ? you dont want to overhead a simple operations.

    But, You could create a generic call for ThreadPool QueueUserItem that receive params, 1 param, 2 param, etc.. This is good instead of sending a simple string and be restricted.

    This how you impl a parameters QueueUserItem with WaitCallback:

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

    taken from C# Execute Method (with Parameters) with ThreadPool

    And some links for ideas:
    http://msdn.microsoft.com/en-us/library/4yd16hza.aspx
    Generic ThreadPool in .NET
    Difference between delegate.BeginInvoke and using ThreadPool threads in C#

    0 讨论(0)
  • 2020-12-24 13:06

    What about this?

    class Program
    {
        static void Main(string[] args)
        {
            ThreadPool.QueueUserWorkItem(MyWork, "text");
            Console.ReadKey();
        }
    
        private static void MyWork(object argument)
        {
            Console.WriteLine("Argument: " + argument);
        }
    }
    

    Or if you do not want to be signature restrictive and have a simple way of putting methods on a thread you can do it like this.For methods that do and do not return a value and have up to 6 parameters it would require you to define 12 overloads if I am not mistaken. It requires a bit more work up front, but is more simple to use.

    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");
        }
    }
    
    0 讨论(0)
  • 2020-12-24 13:12

    I'm not entirely sure what kind of syntax you're looking for, but if you don't like the unused a in your example, why not use Task instead?

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

    It doesn't really seem a lot better, but at least it doesn't have an unused identifier.

    Note: Task.Run() is .Net 4.5 or later. If you're using .Net 4 you have to do:

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

    which isn't as short.

    Both of the above do use the thread pool.

    If you really must avoid using a lambda, you can use an anonymous delegate (which @nowhewhomustnotbenamed already mentioned):

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

    But what's the point of that? It's much less readable!

    0 讨论(0)
  • 2020-12-24 13:21

    I think it's always good to go with the framework and not to "go against it". I'm not saying that the other answers are wrong, they are all calling wrappers around the base method sometimes suppressing the stack in case of issues.

    Have a look at my extension method

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

    There is no guarantee that the email will be sent, also errors are not populated back to the caller, however as a non-blocking method to send emails to all users of a given group... why not. having no error and no slow response sending an email to all users in a given group may just be fine for your use case.

    I would then call the extension method as shown in the controller sample bellow.

    No blocking and in my case, there is no stress if we miss an email as the logs contain the error that the user experience es well.... just who is really looking at logs ;-)

    [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);
       }
    }
    
    0 讨论(0)
提交回复
热议问题