jol*_*olt 13 .net c# asynchronous async-await .net-4.5
这个问题描述了同样的问题 - 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();
Run Code Online (Sandbox Code Playgroud)
该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<bool>>.Factory.StartNew(Listen, connected).Unwrap();
if (stop == true)
{
break;
}
}
// Stop the server.
server.Stop();
Log("Stoppped {0}", endPoint);
}
Run Code Online (Sandbox Code Playgroud)
由于TaskStatus更改为RanToCompletion,因此Task.WhenAll().Wait()
调用标记本身很快完成,导致程序进一步执行,最终终止.
但是,Accept
理论上,任务永远不会停止,它会在明确停止之前监听连接.
这里的问题是什么导致任务被RanToCompletion
过早标记?
Str*_*ior 14
我可以用更少的代码重现这个问题:
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");
}
Run Code Online (Sandbox Code Playgroud)
但这可以正常工作:
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");
}
Run Code Online (Sandbox Code Playgroud)
基本上,通过使用Task.Factory.StartNew()
,您将创建一个Task
基于生成的单独线程来调用给定的委托(Accept()
方法).该Accept
方法本身(像任何好的async
方法)实际上会立即返回.因此,调用它的线程会立即完成其任务,因此Task
表示该线程的创建也会立即完成.
如果你允许Accept()
返回a Task
而不是void
,那么Task
它返回的是你应该等待的,如果你想等到它已经遍历了它await
的所有s.
有两件事是错的:async void
和Task.Factory.StartNew
.这两个都是不好的做法.
首先,async void
不允许调用代码知道它何时完成.所以你在等待并不重要; 该async void
方法将很快返回,您的应用程序无法知道Accept
实际完成的时间.要解决这个问题,请更换async void
更合适的产品async Task
.
其次,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);
Run Code Online (Sandbox Code Playgroud)