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

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.


Ste*_*ary 7

有两件事是错的:async voidTask.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)