草庐IT

c# - 任务应该一直等待吗?

coder 2023-09-19 原文

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

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 等),即使这给了我我想要的行为。是否应始终等待任务?

最佳答案

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

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

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

更好的是,让程序能够优雅地自行关闭。当您希望服务器停止监听时关闭监听套接字。存储 Process() 返回的所有 Task 对象,并在允许进程完成之前等待它们,以确保您的服务器优雅地关闭连接而不是强行重置它们.

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


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

但这是否意味着任务必须总是等待?不,这只是意味着在您的示例中,您没有显示出不这样做的令人信服的理由。在大多数情况下,你应该。如果不出意外,它让您有机会观察可能发生的任何异常(说到这一点,您不应该捕获 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?

关于c# - 任务应该一直等待吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39840678/

有关c# - 任务应该一直等待吗?的更多相关文章

  1. ruby - 其他文件中的 Rake 任务 - 2

    我试图在一个项目中使用rake,如果我把所有东西都放到Rakefile中,它会很大并且很难读取/找到东西,所以我试着将每个命名空间放在lib/rake中它自己的文件中,我添加了这个到我的rake文件的顶部:Dir['#{File.dirname(__FILE__)}/lib/rake/*.rake'].map{|f|requiref}它加载文件没问题,但没有任务。我现在只有一个.rake文件作为测试,名为“servers.rake”,它看起来像这样:namespace:serverdotask:testdoputs"test"endend所以当我运行rakeserver:testid时

  2. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  3. ruby - 如何使用 RSpec::Core::RakeTask 创建 RSpec Rake 任务? - 2

    如何使用RSpec::Core::RakeTask初始化RSpecRake任务?require'rspec/core/rake_task'RSpec::Core::RakeTask.newdo|t|#whatdoIputinhere?endInitialize函数记录在http://rubydoc.info/github/rspec/rspec-core/RSpec/Core/RakeTask#initialize-instance_method没有很好的记录;它只是说:-(RakeTask)initialize(*args,&task_block)AnewinstanceofRake

  4. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  5. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  6. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  7. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  8. ruby-on-rails - 带有 Zeus 的 RSpec 3.1,我应该在 spec_helper 中要求 'rspec/rails' 吗? - 2

    使用rspec-rails3.0+,测试设置分为spec_helper和rails_helper我注意到生成的spec_helper不需要'rspec/rails'。这会导致zeus崩溃:spec_helper.rb:5:in`':undefinedmethod`configure'forRSpec:Module(NoMethodError)对thisissue最常见的回应是需要'rspec/rails'。但这是否会破坏仅使用spec_helper拆分rails规范和PORO规范的全部目的?或者这无关紧要,因为Zeus无论如何都会预加载Rails?我应该在我的spec_helper中做

  9. ruby-on-rails - Rake 任务仅调用一次时执行两次 - 2

    我写了一个非常简单的rake任务来尝试找到这个问题的根源。namespace:foodotaskbar::environmentdoputs'RUNNING'endend当在控制台中执行rakefoo:bar时,输出为:RUNNINGRUNNING当我执行任何rake任务时会发生这种情况。有没有人遇到过这样的事情?编辑上面的rake任务就是写在那个.rake文件中的所有内容。这是当前正在使用的Rakefile。requireFile.expand_path('../config/application',__FILE__)OurApp::Application.load_tasks这里

  10. ruby-on-rails - 在 Ruby on Rails 中发送响应之前如何等待多个异步操作完成? - 2

    在我做的一些网络开发中,我有多个操作开始,比如对外部API的GET请求,我希望它们同时开始,因为一个不依赖另一个的结果。我希望事情能够在后台运行。我找到了concurrent-rubylibrary这似乎运作良好。通过将其混合到您创建的类中,该类的方法具有在后台线程上运行的异步版本。这导致我编写如下代码,其中FirstAsyncWorker和SecondAsyncWorker是我编写的类,我在其中混合了Concurrent::Async模块,并编写了一个名为“work”的方法来发送HTTP请求:defindexop1_result=FirstAsyncWorker.new.async.

随机推荐