任务应该一直等待吗?

Should Task always be Awaited?

我正在创建一个可以有多个客户端的异步服务器。类似于聊天 client/server 架构,所有客户端都会根据任何客户端的请求在每次服务器状态更改时更新。我找到了很多示例,并编写了一个简单的测试应用程序。客户端请求的处理暂时写完了,遇到了平时不会遇到的情况。这是我写的示例服务器:

class Server
{
    int _port;
    TcpListener _listener;
    IList<TcpClient> _clients = new List<TcpClient>();

    public Server(int port)
    {
        _port = port;
        _listener = new TcpListener(IPAddress.Any, _port);
    }

    public async Task StartListening()
    {
        _listener.Start();
        Console.WriteLine("The server is listening on port {0}...", _port);

        while (true)
        {
            try
            {
                var client = await _listener.AcceptTcpClientAsync();
                Console.WriteLine("We have a client!");
                _clients.Add(client);
                Process(client);

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }
        }
    }

    private async Task Process(TcpClient client)
    {
        try
        {
            var stream = client.GetStream();
            var reader = new StreamReader(stream);
            var writer = new StreamWriter(stream) { AutoFlush = true };
            char[] buffer = new char[1024];
            while (true)
            {
                var request = await reader.ReadLineAsync();
                if (request != null)
                {
                    Console.WriteLine(request);
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
            client.Close();
        }
    }

}

这里是Program.cs:

class Program
{
    static void Main(string[] args)
    {
        var server = new Server(6029);
        server.StartListening().Wait();
    }
}

由于未等待任务,我在进程调用中收到警告。我理解没有等待调用的代码的行为,但我想知道我是否应该以不同的方式编码(ThreadPool 等),即使这给了我我想要的行为。是否应始终等待任务?

很难知道你在这里真正问的是什么。您问了 "I'm wondering if I should be coding this differently" 隐含的具体问题,以及广泛的、主要基于意见的问题 "Should Tasks always be awaited?"

对于后者,唯一可以认为是正确的答案是 "no." 在编程中几乎没有什么 总是 必须完成的。

就是说,您发布的代码确实有缺陷。首先,您有一个永远不可能完成的 async Task 方法。那有什么意义呢?你不妨声明一下async void。该程序可以通过其他一些机制无限期地等待,例如无限长时间地休眠或阻塞 Console.ReadLine() 方法或其他东西。

更好的是,为程序提供一种正常关闭自身的方法。当您希望服务器停止侦听时关闭侦听套接字。存储 Process() 返回的所有 Task 对象,并在允许进程完成之前等待它们,以确保您的服务器正常关闭连接而不是强行重置它们。

您发布的代码本身不够具体,无法提供任何具体的建议。它看起来像入门代码,主要用于演示一些基本概念,而不是做任何实际的事情。因此,它必然会受到与应该在所有情况下都能正常工作的代码不同的规则的约束。


在我看来,鉴于您的代码示例,我 在某个时候等待您创建的任务。您不一定需要使用 await Process(...)(实际上,您可能不想这样做,因为那样会阻止您一次处理多个客户端),但您应该保留引用并最终等待。

但这是否意味着 Task 必须 总是 等待?不,这只是意味着在您的示例中,您没有显示出不这样做的令人信服的理由。在大多数情况下,你应该。如果不出意外,它让您有机会观察可能发生的任何异常(说到这一点,您不应该捕获 Exception ......只捕获那些您期望并且您确定如何处理的异常) .但在极少数情况下,如果只是出于纯粹的实用性(或者更确切地说,试图观察任务的不切实际),那么一旦开始就不再关注你已经开始的任务是有意义的。


补充阅读:
How to safely call an async method in C# without await
warning this call is not awaited, execution of the current method continues
Where to stop using async /await keywords?