如何排队/等待TPL任务完成

时间:2012-10-09 00:53:03

标签: c# .net concurrency task-parallel-library

我需要一些关于TPL和任务的帮助

以下是我的方案:

  1. 我有一些事件处理程序可以产生任务。如果在上一个任务完成之前调用了一个事件,我想在继续之前等待(阻止)它。

  2. 我有一个同步方法,在调用之前,必须等到任何事件处理程序中的任何衍生任务完成后再继续。


  3.  
    public void DoSomething()
    {
        // Expects any running tasks from OnEvent1(), OnEvent2(), OnEvent3()
        // to be completed before proceeding.
    }
    
    public void OnEvent1()
    {
        Task.Factory
        .StartNew(()=>{ /*Long running task*/ })
        .ContinueWith(task=>{ /* Updates UI */ });
    }
    
    public void OnEvent2()
    {
        Task.Factory
        .StartNew(()=>{ /*Long running task*/ })
        .ContinueWith(task=>{ /* Updates UI */ });
    }
    
    public void OnEvent3()
    {
        Task.Factory
        .StartNew(()=>{ /*Long running task*/ })
        .ContinueWith(task=>{ /* Updates UI */ });
    }
    

    实际情况是:

    • OnFetchData() =>产卵任务。所有后续调用都需要排队。
    • OnSyncSettings() =>产卵任务。所有后续调用都需要排队。

    • OnAutoBackup() =>同步方法。在保存之前,等待任何其他任务(例如,获取数据/同步设置)完成。

    • OnFullBackup() =>同步方法。手动运行FetchData()和SyncSettings(),等待完成,继续。

    我的问题很简单:我该怎么做?

    这种方法是否正确?

    1. 每个事件处理程序都会记住它的最后一个。调用时,它将等待其列表中的所有任务完成后再继续。

    2. 对于同步方法,它需要等待所有任务(从每个事件处理程序)到


    3. Task _lastEvent1Task;
      public void OnEvent1()
      {
          // Wait for all tasks to complete before proceeding
          if (_lastEvent1Task!=null)
          _lastEvent1Task.Clear();
      
          // Spawns new task
          _lastEvent1Task = Task.Factory.StartNew(()=>{ });
      }
      
      public void OnEvent3()
      {
          // Wait for any running tasks to complete before proceeding
          if (_lastEvent1Task != null) _lastEvent1Task.Wait();
          if (_lastEvent2Task != null) _lastEvent2Task.Wait();
          ...
          // Proceed
      
      }
      

      谢谢!


      [编辑]

      当DoSomething()等待并且引发Event1时? 如果DoSomething()正在运行并且引发了一个事件怎么办?

      OnEvents()

      • 如果第一个任务:生成任务并异步执行
      • 如果上一个任务仍在运行,则排队/阻止。关键部分。
      • 假设:
        • 这些事件通常不会多次出现。
        • 预计用户界面会阻止用户在仍在运行时引发相同的事件。

      DoSomething()(调用时)

      • 假设:禁用事件通知。
      • 假设:没有新事件排队。
      • 预期等待所有剩余的排队任务完成后再继续
      • 预期:是同步通话,在通过
      • 执行之前不会返回来电

2 个答案:

答案 0 :(得分:2)

您描述的方法存在并发问题。例如,在_lastEvent1Task上进行空检查后,其他一些线程可能会将_lastEvent1Task设置为空,从而导致NullReferenceException

我假设每个任务都应该同步运行,但有些不应该阻塞。实现此目的的最佳方法是使用仅使用单个线程的TaskScheduler,您可以找到示例here

var scheduler = new SingleThreadedTaskScheduler();
var taskFactory = new TaskFactory(scheduler);

对于非阻塞方法,您可以得到如下内容:

public void NonBlockingMethod()
{
    taskFactory
        .StartNew(() => { /* Long-running work. */ })
        .ContinueWith(t => { /* Update UI. */ });
}

对于阻止方法:

public void BlockingMethod()
{
    taskFactory
        .StartNew(() => { /* Do work. */ })
        .Wait();
}

当您安排任务时,任务调度程序将仅使用一个线程来安排它们,从而保证所有任务同步运行。

答案 1 :(得分:0)

我认为你需要使用ParExtSamples中的任务调度程序。 您通常需要 OnEvents() OrderedTaskScheduler ,然后等待所有这些计划任务在 DoSomething()方法中完成,或者只需在同一 OrderedTaskScheduler 上安排 DoSomething()方法,只有在完成之前安排的任务完成后才会执行。