c#多个任务在后台独立运行而不会互相阻塞

时间:2020-08-09 22:33:44

标签: c# multithreading async-await windows-services task-parallel-library

我有一个C#Windows服务,可以在其中运行一些任务。

其中一项任务是无限异步循环,其他任务是通过计时器触发的,然后执行任务。

     private readonly QueueProcessor _queueProcessor;

     protected override void OnStart(string[] args)
            {
               // first task
                _queueTask = _queueProcessor.Run(_cancellation.Token);
    
                // second task
                affiliate_timer = new System.Timers.Timer();
                affiliate_timer.AutoReset = true;
                affiliate_timer.Interval = _model.Interval_Affiliate * 60000;
                affiliate_timer.Elapsed += new 
                System.Timers.ElapsedEventHandler(affiliate_timer_Elapsed);

                // third task
                invoice_timer = new System.Timers.Timer();
                invoice_timer.AutoReset = true;
                invoice_timer.Interval = _model.Interval_Invoice * 60000;
                invoice_timer.Elapsed += new 
                System.Timers.ElapsedEventHandler(invoice_timer_Elapsed);
            }
    

private void invoice_timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
            {
               
                if (!_isAffiliateBusy)
                {
                    _isAffiliateBusy= true;
    
                    var task = Task.Run(() => StartAffiliateTask());
                    task.Wait();
    
                    _isAffiliateBusy= false;
                }
                 
            }

     
     private void invoice_timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
            {
               
                if (!_isInvoiceBusy)
                {
                    _isInvoiceBusy = true;
    
                    var task = Task.Run(() => StartInvoiceTask());
                    task.Wait();
    
                    _isInvoiceBusy = false;
                }
                 
            }
    

  private void StartAffiliateTask()
            {
                _affiliateModule = new Modules.Affiliate();
                _affiliateModule.RunSync();
            }

     private void StartInvoiceTask()
            {
                _invoiceModule = new Modules.Invoice();
                _invoiceModule.RunSync();
            }

这是我的QueueProcessor类,实现了await/async以执行无限循环作业:

  public class QueueProcessor
    {
        private readonly IQueueBroker _serviceBroker;

        public QueueProcessor()
        {
        }

        public async Task Run(CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                var receiveMessageResponse = await _serviceBroker.ReceiveMessageAsync("test", cancellationToken);
                if (!receiveMessageResponse.Messages.Any())
                {
                    continue;
                }

                foreach (var message in receiveMessageResponse.Messages)
                {
                    
            // some other tasks here...

                    await _serviceBroker.DeleteMessageAsync(message, cancellationToken);
                }
            }
        }
    }

我的AffiliateInvoice模块类没有在其中实现以下任何await/async代码:

public class Affiliate
    {
        /// <summary>
        /// Start the sync process
        /// </summary>
        public void RunSync()
        {  
            try
            {
                // some code here...
            }
            catch (Exception ex)
            {
                
            }
           
        }
 }

我的问题是:

当我的队列处理器无限循环正在运行时,由计时器触发的其他任务是否仍然可以独立运行?

当我使用时:

 var task = Task.Run(() => StartAffiliateTask());
                    task.Wait();

Wait方法是否停止整个服务线程,直到此任务完成?还是不会阻止我的StartInvoiceTask独立运行?

有没有建议让我的3个任务彼此独立运行的最佳方法?

1 个答案:

答案 0 :(得分:1)

总结多个潜在问题:

  1. 种族状况(访问/写入_isBusy)。
  2. 潜在的死锁(较小的ThreadPool大小)。
  3. 如果发生错误或线程中断(_isBusy可以保持为'true'状态),则标志的潜在不连续状态。

此外,我假设您的“任务”应在单个实例中运行,因此如果计时器回调仍在运行,我们将不赞成这样做。 您应该像这样更改计时器事件处理程序(最好将其包装在某种类中):

        //the flag, do mention volatile modifier - it tells particular 
        //systems to watch for variable changes by reference, 
        //instead of just copying it into thread stack by value.
        private volatile bool _isAffiliateBusy = false;

        //sync object for flag to eliminate race condition
        private object _affiliateSync = new object(); 
        private void affiliate_timer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
        {
            //very fast lookup at flag to filter threads which comes when task is still processing
            if(_isAffiliateBusy)
                return;
            lock(_affiliateSync) //taking lock
            {
                //checking again for those threads which 'happen' to be faster than you think.
                if(_isAffiliateBusy)
                    return;
                //aquire lock for business 'task'
                _isAffiliateBusy = true;
            }
            try
            {
                StartAffiliateTask();
            }
            finally
            {
                //resetting singleton business 'task' lock.
                //do not forget to use finally block, to handle disposing
                //even if something rise up in 'try' section - you will not be left with invalid state of flag.
                _isAffiliateBusy = false; 
            }
        }
相关问题