问题描述了此处发现的同一问题 - MSDN Developer Forum。这个问题没有得到公认的答案,所给出的任何答案都不适用于我的案例(因此是一个不同的问题)。
问题也来自一个asked previously,但是,由于性质和具体问题的不同,我会问一个新问题。
完整代码可在此处找到: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<bool>>.Factory.StartNew(Listen, connected).Unwrap();
if (stop == true)
{
break;
}
}
// Stop the server.
server.Stop();
Log("Stoppped {0}", endPoint);
}
由于TaskStatus更改为RanToCompletion,Task.WhenAll().Wait()
调用标记本身很快完成,导致程序被进一步执行,最终终止。
但是,理论上,Accept
任务永远不会停止,它会在明确停止之前监听连接。
这里的问题是什么导致任务过早地被标记为RanToCompletion
?
答案 0 :(得分:12)
我可以用更少的代码重现这个问题:
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
s。
答案 1 :(得分:5)
有两个错误:async void
和Task.Factory.StartNew
。这些都是不好的做法。
首先,async void
does not allow the calling code to know when it completes。所以你在等待并不重要; Accept
方法会很快返回,而您的应用无法知道async void
何时实际完成。要解决此问题,请将async Task
替换为更合适的StartNew
。
第二,StartNew
doesn't understand asynchronous delegates。 Task.Run
是一个极低级别的API,不应在99.99%的生产代码中使用。请改用public static async Task Accept(object state);
Task t1 = Task.Run(() => Accept(s1));
Task t2 = Task.Run(() => Accept(s1));
Task.WaitAll(t1, t2);
。
$value_to_encode = 'some=string/here+foobar';
$encoded = urlencode(base64_encode($value_to_encode));
$decoded = base64_decode(urldecode($encoded));