5

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

这是我的场景:

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

  2. 我有一个同步方法,当调用它时,必须等到任何事件处理程序的任何派生任务完成后,才能继续。


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. 对于同步方法,它需要等待所有任务(来自每个事件处理程序)


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() 正在运行并引发事件怎么办?

事件()

  • 如果是第一个任务:生成任务并异步执行
  • 如果之前的任务仍在运行,则排队/阻塞。临界区。
  • 认为:
    • 这些事件通常不会多次引发。
    • UI 应防止用户在仍在运行时引发相同的事件。

DoSomething()(调用时)

  • 假设:事件通知被禁用。
  • 假设:没有新事件将排队。
  • 预期:等到所有剩余的排队任务完成后再继续
  • 预期:是一个同步调用,直到通过执行才返回调用者
4

2 回答 2

3

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

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

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

当你调度任务时,任务调度器将只使用一个线程来调度它们,保证所有任务同步运行。

于 2012-10-09T14:31:54.350 回答
0

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

于 2012-10-10T14:12:14.857 回答