Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 在非UI应用程序上优雅地重新启动取消的异步任务的建议模式_C#_Multithreading_Async Await - Fatal编程技术网

C# 在非UI应用程序上优雅地重新启动取消的异步任务的建议模式

C# 在非UI应用程序上优雅地重新启动取消的异步任务的建议模式,c#,multithreading,async-await,C#,Multithreading,Async Await,我有一个控制台应用程序,可以实例化WeatherClientManager类。 console应用程序中的主线程请求WeatherClientManager类中的当前天气状态,但WeatherClientManager类持续从服务器接收数据。 代码: public static void Main(string []) { Program p = new Program(); Task.Run(()=>p.RunLoop()); }

我有一个控制台应用程序,可以实例化WeatherClientManager类。 console应用程序中的主线程请求WeatherClientManager类中的当前天气状态,但WeatherClientManager类持续从服务器接收数据。 代码:

 public static void Main(string [])
    {
        Program p = new Program();
        Task.Run(()=>p.RunLoop());


    }

    class Program{


        WeatherClientManager wcM; 
        public void RunLoop()
        {

            wcM = new WeatherClientManager ();
            await wcM.InitiateConnection().ConfigureAwait(false);

        }
    }

    class WeatherClientManager 
    {
        public async Task<bool> InitiateConnection()
        {
            TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())

            await tcpClient.ConnectAsync()

            CancellationTokenSource cts = new CancellationTokenSource();
            if(tcpClient.Connected)
            {
               Task.Run(()=>ReceiveTask(cts.Token));
               Task.Run(()=>SendKeepAlive(cts.Token));
               return true;
            }
            return false;
        }

        private void ReceiveTask(CancellationToken t)
        {
            try{
                networkStream.Receive(..) // throws exception
            }
            catch(Exception e)
            {
               Stop(e);
            }
        }


        private void SendKeepAlive(CancellationToken t)
       {
            while(!t.IsCancellationRequested)
            {
                try{
                    networkStream.Write(..) // throws exception
                }
                catch(Exception e)
                {
                   Stop(e);
                }
            }
       }

       private void Stop(Exception e )
       {
         log.Error(e);
         e.Cancel();

       }
    }
但是我讨厌旋转一个任务来控制两个子任务的想法。我的问题是在哪里以及如何重新启动连接。有什么想法吗

编辑: 我已经更新了代码,使WeatherClientManager具有OnDisconnectDetected事件。因此Program.cs类订阅如下:

weatherServerManager.OnDisconnectDetected += HandleDisconnectDetection

public async void HandleDisconnectDetection()
{
    wsM = new WeatherClientManager ();
    wsM.InitiateConnection().ConfigureAwait(false);
}

 private void SendKeepAlive(CancellationToken t)
 {
     while (...)
     {
         try{}
         catch(Exception e)
         {
           OnDisconnectDetected?.Invoke();
         }
     }
}
当WeatherClientManager调用处理程序时,它将创建一个新任务,该任务应在不同的上下文中继续。此时应退出KeepAlive任务


仍然感觉不舒服,但欢迎想法

一般来说,我更喜欢方法的组合而不是引发事件。特别是,请避免基于Task.Run的火灾并忘记

对于异步套接字,我认为给每个套接字一个主循环是有意义的:

class WeatherClientManager
{
  public async Task MainLoop()
  {
    TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())
    await tcpClient.ConnectAsync();

    CancellationTokenSource cts = new CancellationTokenSource();
    var receiveTask = Task.Run(()=>ReceiveTask(cts.Token));
    var keepaliveTask = Task.Run(()=>SendKeepAlive(cts.Token));
    await Task.WhenAll(receiveTask, keepaliveTask);
  }
}
class Program
{
  public async Task RunLoop()
  {
    while (true)
    {
      wcM = new WeatherClientManager();
      await wcM.MainLoop();
    }
  }
}
然后可将其组成主程序的主循环:

class WeatherClientManager
{
  public async Task MainLoop()
  {
    TCPClient tcpClient = new TcpClient(GetTCPDetailsFromConfig())
    await tcpClient.ConnectAsync();

    CancellationTokenSource cts = new CancellationTokenSource();
    var receiveTask = Task.Run(()=>ReceiveTask(cts.Token));
    var keepaliveTask = Task.Run(()=>SendKeepAlive(cts.Token));
    await Task.WhenAll(receiveTask, keepaliveTask);
  }
}
class Program
{
  public async Task RunLoop()
  {
    while (true)
    {
      wcM = new WeatherClientManager();
      await wcM.MainLoop();
    }
  }
}
依次由以下主要部分组成:


通过避免火灾和遗忘,您可以确保您的代码始终遵守所有异常。忽略任务偶尔是可以的,但通常是错误的。

检查Polly@MrinalKamboj具体是哪一部分?重试-好的,谢谢。出于学习的目的,我想要一个非图书馆的解决方案:为了照顾这些随机的人,我必须投票表决各种问题和答案。否决一切都很糟糕,它不允许人们学到任何东西。我必须说嗨,斯蒂芬,我希望你能回答。谢谢:我在跟踪RunLoop的控制流时遇到了一些问题。wait wcM.MainLoop是否退出while块?所以如果我有:p.RunLoop p.DoSomethingElse,DoSomethingElse会执行吗?不过,我相信你已经给了我一个如何进行的想法。我到家后将尝试:当ReceiveTask和SendKeepAlive完成时,即套接字关闭时,Main循环完成。除非出现意外异常,否则运行循环永远不会完成。谢谢Stephen。你在async await生成的状态机上有博客吗?@Lewsterin:没有;我认为没有必要学习异步。我有。如果需要状态机详细信息。