按顺序处理任务

本文关键字:任务 顺序处理 | 更新日期: 2023-09-27 18:14:38

我有一些事件通知每次发生,例如一个文件被添加到一个文件夹(这不是我的情况,但只是一个类比来解释)。

文件夹中可以添加任意数量的文件,我们不知道会有多少个。

目前,我的事件myEvent_Handler在每次添加文件时触发

private void myEvent_Handler(object sender, SomeEvent e)
{
  // I dont know how many times this event will fire
  if (something == true) (1)
  {
    var t = Task.Run(() =>
    {
      DoSomething(e);  (2)
    });
  }
}

上述事件处理程序中的问题是(1)和(2)标记的行中的竞争条件。例如,事件被触发4次:

  1. (1)为true,(2)在任务队列中运行
  2. (1)为true,(2)在任务队列中运行
  3. (1)为true,(2)在任务队列中运行
  4. (1)为true,(2)在任务队列中运行

现在,任务队列有4个任务要运行,但这些任务的执行顺序不同。它们可以按任何顺序执行,即3->2->4->1,但我需要它们按1->2->3->4执行,而不阻塞UI线程。

如何在不阻塞UI线程的情况下实现这一点?

按顺序处理任务

我会考虑使用微软的响应式框架(NuGet "System.Reactive")。这是理想。

Observable
    .FromEventPattern<EventHandler, EventArgs>(
        h => myEvent.Handler += h, h => myEvent.Handler -= h)
    .ObserveOn(Scheduler.Default)
    .Subscribe(ep => DoSomething(ep.EventArgs));

它将自动编组到一个后台线程(Scheduler.Default),并保证每个订阅调用都要等待,直到上一个调用完成后才能继续。

这将完全取代myEvent_Handler方法。

.Subscribe(...)调用返回IDispose,您可以使用它在任何时候停止处理-它有效地从事件分离。

我弄明白了,并重写了上面的代码:

private void myEvent_Handler(object sender, SomeEvent e)
{
  // I dont know how many times this event will fire
  Task t = new Task(() =>
  {
    if (something == true) 
    {
        DoSomething(e);  
    }
  });
  t.RunSynchronously();
}

这是伟大的工作,并没有阻止我的UI线程。

更新:虽然我发现这对我来说很有用(它固定了我的竞争条件,我的UI线程没有被阻塞),经过进一步的调查,我发现调用此代码的方法没有在UI线程上执行(这解释了为什么此代码没有阻塞我的UI线程),但检查ManagedThreadId,我发现上面的任务在同一线程上运行这个方法正在运行。因此,我根据这两个帖子改变了我的实现:

https://msdn.microsoft.com/library/system.threading.tasks.taskscheduler.aspx

如何(如果)使用TPL写一个单消费者队列?

使用LimitedConcurrencyLevelTaskScheduler(在上面的MSDN文章中提供)

这个解决方案工作得很好,任务不是在同一个线程上执行的,任务在其中实例化的方法提高了性能。