【问题标题】:Should Task always be Awaited?是否应该始终等待任务?
【发布时间】:2017-02-11 22:24:26
【问题描述】:

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

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();
    }
}

我在 Process 调用中收到警告,因为没有等待 Task。我在没有 await 调用的情况下理解代码的行为,但我想知道是否应该以不同的方式编码(线程池等),即使这给了我想要的行为。是否应始终等待任务?

【问题讨论】:

  • Should Tasks always be awaited? No...Fire&Forget 任务也有一些用法..

标签: c# asynchronous tcp client-server task


【解决方案1】:

很难知道您在这里真正要问的是什么。您已经问了 “我想知道我是否应该以不同的方式编码” 所暗示的具体问题,以及广泛的、主要基于意见的问题 “是否应该一直等待任务?”

对于后者,唯一可能正确的答案是“不”。编程中几乎没有什么事情是总是必须要做的。

也就是说,您发布的代码确实存在缺陷。一方面,您有一个不可能完成的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?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-07-22
    • 2015-03-17
    • 2016-02-16
    • 1970-01-01
    • 1970-01-01
    • 2019-06-05
    • 2012-01-03
    相关资源
    最近更新 更多