我有一个必须并行运行代码块的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
完成后,我想再次运行它。事实上,我想反复运行它直到程序停止。但是,我希望MySecondTask
与MyFirstTask
并行运行。我的问题是,如何重复执行MyFirstTask
,同时仍然与MySecondTask
并行?
我回顾了几个相关的SO问题。我也没有看到Complete
种事件处理程序。所以,我在如何实现这一点方面有点迷失。感谢您的帮助!
答案 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