如果任务等待某事,任务状态将更改为RanToCompletion
这个问题描述了同样的问题 – MSDN Developer Forum 。 这个问题没有得到公认的答案,所给出的任何答案都不能适用于我的案例(因此是一个不同的问题)。
问题也来自我之前提出的问题 ,但是,由于性质和具体问题的不同,我问了一个新问题。
完整代码可以在这里找到: http : //pastebin.com/uhBGWC5e
*唯一改变的是任务完成检查( while
– > Task.WhenAll
)。
等待任务内部的异步操作时,即使任务仍在运行,任务状态也会更改为RanToCompletion
。
现在,让我们看看设置:
// Start async. Task t1 = Task.Factory.StartNew(Accept, s1); Task t2 = Task.Factory.StartNew(Accept, s1); Task.WhenAll(t1, t2).Wait();
Accept
方法:
public static async void Accept(object state) { TcpListenerEx server = (TcpListenerEx) state; IPEndPoint endPoint = server.LocalEndpoint as IPEndPoint; Log("Accepting clients on {0}", endPoint); while (true) { var client = server.AcceptTcpClientAsync(); if (client == null) { Log("Null error on accept"); break; } TcpClient connected = (TcpClient) client; servers[server].Add(connected); bool stop = await Task<Task>.Factory.StartNew(Listen, connected).Unwrap(); if (stop == true) { break; } } // Stop the server. server.Stop(); Log("Stoppped {0}", endPoint); }
由于TaskStatus更改为RanToCompletion, Task.WhenAll().Wait()
调用标记本身很快完成,导致程序进一步执行,最终终止。
但是,理论上, Accept
任务永远不会停止,它会在明确停止之前监听连接。
这里有什么问题导致Task过早地被标记为RanToCompletion
?
我可以用更少的代码重现这个问题:
void Main() { Task t1 = Task.Factory.StartNew(Accept); t1.Wait(); Console.WriteLine("Main ended"); } public static async void Accept() { while (true) { await Task.Delay(1000); } Console.WriteLine("Stoppped"); }
但这可以正常工作:
void Main() { Task t1 = Accept(); t1.Wait(); Console.WriteLine("Main ended"); } public static async Task Accept() { while (true) { await Task.Delay(1000); } Console.WriteLine("Stoppped"); }
基本上,通过使用Task.Factory.StartNew()
,您将基于生成的单独线程创建一个Task
来调用给定的委托( Accept()
方法)。 Accept
方法本身(就像任何好的async
方法一样)实际上会立即返回。 因此,调用它的线程会立即完成其任务,因此创建表示该线程的Task
也会立即完成。
如果你允许Accept()
返回一个Task
而不是void
,那么它返回的Task
是你应该等待它等待它运行所有await
的时候。
有两件事是错误的: async void
和Task.Factory.StartNew
。 这些都是不好的做法。
首先, async void
不允许调用代码知道它何时完成 。 所以你在等待并不重要; async void
方法将很快返回,并且您的应用程序无法知道Accept
实际完成的时间。 要解决此问题,请使用更合适的async Task
替换async void
。
其次, StartNew
不了解异步委托 。 StartNew
是一个极低级别的API,不应在99.99%的生产代码中使用。 请改用Task.Run
。
public static async Task Accept(object state); Task t1 = Task.Run(() => Accept(s1)); Task t2 = Task.Run(() => Accept(s1)); Task.WaitAll(t1, t2);