结束时反复调用C#任务

时间:2018-05-01 12:27:17

标签: c# task-parallel-library

我有一个必须并行运行代码块的C#应用​​程序。这是其中两个代码块的基本结构。实际上,还会有更多。

private async Task MyFirstTask()
{
   // do stuff

   await FirstTaskImplementation();

   // cleanup
}

private async Task MySecondTask()
{
   // do stuff

   await SecondTaskImplementation();

   // cleanup
}

其中一些代码块将在计时器上运行。有些会反复运行。为了实现这一目标,我有以下几点:

Task.Run(() => MyFirstTask());
Task.Run(() => MySecondTask());

MyFirstTask完成后,我想再次运行它。事实上,我想反复运行它直到程序停止。但是,我希望MySecondTaskMyFirstTask并行运行。我的问题是,如何重复执行MyFirstTask,同时仍然与MySecondTask并行?

我回顾了几个相关的SO问题。我也没有看到Complete种事件处理程序。所以,我在如何实现这一点方面有点迷失。感谢您的帮助!

3 个答案:

答案 0 :(得分:1)

async/await的美妙之处在于,您可以编写异步代码,就像编写同步代码一样。你会如何重复同步操作?你可以使用一个循环。你可以在这里做同样的事情,例如:

private async Task MyFirstTask(CancellationToken token) {
    while (!token.IsCancellationRequested) {
       // do stuff
       await FirstTaskImplementation();
       // cleanup
    }
}

您可以在当前方法中嵌入循环或将其提升为包装方法,但它应该以任何方式工作。

您可以按照现在的相同方式继续安排任务,但您确实应该await async方法:

CancellationTokenSource cts = new CancellationTokenSource();

Task.Run(async () => await MyFirstTask(cts.Token));
Task.Run(async () => await MySecondTask());

// Request cancellation via `cts` when you want the looping to end.

虽然你没有问过它,但是如果你想在每次迭代之间插入一个延迟,你可以简单地在循环体的末尾放置一个await Task.Delay(...)语句。

答案 1 :(得分:0)

您不一定需要使用Task.Run来完成第一项任务。您可以使用Timer作为第一项任务,AutoReset设置为true。这样它就会永远运行,你再也不用担心了。

private static System.Timers.Timer aTimer;

public static void Main()
{
    SetTimer(); //MyFirstTask starts running over and over in another thread
    Task.Run(() => MySecondTask());
}

private static void SetTimer()
{
    // Create a timer with a 1ms interval (has to be > 0)
    aTimer = new System.Timers.Timer(1);
    // Hook up the Elapsed event for the timer. 
    aTimer.Elapsed += MyFirstTask;
    aTimer.AutoReset = true;
    aTimer.Enabled = true;
}

private static async void MyFirstTask(Object source, ElapsedEventArgs e)
{
   // do stuff

   await FirstTaskImplementation();

   // cleanup
}

private async Task MySecondTask()
{
   // do stuff

   await SecondTaskImplementation();

   // cleanup
}

答案 2 :(得分:0)

另一种方法是这样的:

var first=MyFirstTask().ToObservable();
var second=MySecondTask().ToObservable();

first.Repeat().Merge(second).Subscribe(x=>Console.WriteLine("Task completing."));

这是说明性的,未经过测试的代码。如果您希望MySecondTask完成,那么可能就是这样:

first.Repeat().TakeUntil(second).Subscribe(x=>Console.WriteLine("Task completing."));

如果您想将秒数添加到秒,您可以这样做:

first.Repeat().TakeUntil(second.Timeout(TimeSpan.FromMilliseconds(100))).Subscribe(...)

如果要在每个任务完成时显示某些内容,请将observable声明为:

var first=MyFirstTask().ToObservable().Do(Console.WriteLine("First completing"));

以上需要System.Reactive.Linq命名空间。我发现这些基于Rx的并发解决方案通常比TPL更优雅,但这只是主观的。

最后,如果您不想在调用订阅之前启动任务,或者已准备好开始观看,则可以使用Observable.FromAsync,as per info here