如果任务等待某事,任务状态将更改为RanToCompletion

时间:2014-05-29 15:05:29

标签: c# .net asynchronous async-await .net-4.5

问题描述了此处发现的同一问题 - 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

2 个答案:

答案 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 voidTask.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 delegatesTask.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));