C# 在非UI应用程序上优雅地重新启动取消的异步任务的建议模式
我有一个控制台应用程序,可以实例化WeatherClientManager类。 console应用程序中的主线程请求WeatherClientManager类中的当前天气状态,但WeatherClientManager类持续从服务器接收数据。 代码: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()); }
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:没有;我认为没有必要学习异步。我有。如果需要状态机详细信息。