我很困惑这种情况,其中一个类有一个方法可以启动两个定期Task
,然后一个属性用于检查两个Task
是否仍在运行,但是结果出人意料。这是代码(简化):
public partial class UdpClientConnector
{
Task localListener;
Task periodicSubscriber;
bool keepWorking = false;
public bool IsRunning
{
get
{
if ((localListener != null) && (periodicSubscriber != null))
{
return (localListener.Status == TaskStatus.Running) &&
(periodicSubscriber.Status == TaskStatus.Running);
}
else
return false;
}
}
public void Start()
{
keepWorking = true;
localListener = new Task(() => LocalListenerWorker());
localListener.Start();
periodicSubscriber = new Task(() => PeriodicSubscriberWorker());
periodicSubscriber.Start();
}
public void Stop()
{
keepWorking = false;
localListener.Wait();
periodicSubscriber.Wait();
}
async void LocalListenerWorker()
{
while (keepWorking)
{
// Do some work and then wait a bit
await Task.Delay(1000);
}
}
async void PeriodicSubscriberWorker()
{
while (keepWorking)
{
// Do some (other) work and then wait a bit
await Task.Delay(1000);
}
}
}
为了测试这个样板,我使用了以下内容:
UdpClientConnector connector = new UdpClientConnector();
// This assert is successful because the two Tasks are not yet started
Assert.IsTrue(!connector.IsRunning);
// Starts the tasks and wait a bit
Connector.Start();
Task.Delay(2000).Wait();
// This fails
Assert.IsTrue(connector.IsRunning);
当我尝试调试测试用例时,我发现两个任务处于RanToCompletion
状态,这是意外的,因为这两个任务都只是循环而不应该终止keepWorking
1}}变得虚假。
我也尝试使用Task.Factory.StartNew(..)
启动任务并获得相同的结果。
我缺少什么?谢谢!
答案 0 :(得分:4)
问题在于如何启动任务,以及任务方法。
localListener = new Task(() => LocalListenerWorker());
当LocalListenerWorker
返回时,该任务将完成 - 它将立即执行 (当它遇到第一个await
表达式时)。它不等待异步操作实际完成(即完成循环)。
async void
方法几乎永远不会使用 - 它们基本上只用于支持事件处理程序。
我建议您重写方法以返回Task
,然后使用Task.Run
启动它们,传入方法组:
Task.Run(LocalListenerWorker);
...
private async Task LocalListenerWorker()
{
// Body as before
}
Task.Run
的任务只有在LocalListenerWorker
返回的任务完成时才会完成,即循环体完成时。
这是一个完整的演示:
using System;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
Task task1 = Task.Run(Loop);
// Don't do this normally! It's just as a simple demo
// in a console app...
task1.Wait();
Console.WriteLine("First task done");
Task task2 = new Task(() => Broken());
task2.Start();
// Don't do this normally! It's just as a simple demo
// in a console app...
task2.Wait();
Console.WriteLine("Second task done");
}
static async Task Loop()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine(i);
}
}
static async void Broken()
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
Console.WriteLine(i);
}
}
}
输出显示:
0
1
2
3
4
First task done
Second task done
第一个任务按预期运行,仅在第一个异步方法确实完成时才完成。第二个任务的行为类似于您当前的代码:它在第二个异步方法返回后立即完成 - 由于await
几乎立即发生。