结束时重复调用 C# 任务

Call C# Task Repeatedly on conclusion

我有一个 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 类型的事件处理程序。所以,我对如何实现这个有点迷茫。感谢您的帮助!

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(...) 语句。

您不一定需要为第一个任务使用 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
}

另一种方法是这样的:

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(...)

如果您想在每个任务完成时显示一些内容,请将可观察对象声明为:

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

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

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