如何在C#异步控制台应用中异步运行带回调的WebSocket任务?
解决异步控制台应用中WebSocket回调不阻塞主逻辑的问题
我来帮你搞定这个问题!你的核心需求是让Binance WebSocket的订阅逻辑不阻塞MainAsync里的后续代码,对吧?咱们可以把WebSocket的逻辑包装成异步任务,同时用取消令牌来优雅地控制它的生命周期,完美适配你的异步控制台架构。
整合后的完整代码示例
using Nito.AsyncEx; using ExchangeSharp; using System.Threading; using System.Threading.Tasks; class Program { static void Main(string[] args) { // 用AsyncContext托管异步主逻辑,解决控制台应用的异步入口问题 AsyncContext.Run(() => MainAsync()); } static async Task MainAsync() { // 创建取消令牌源,用来统一控制WebSocket的生命周期 using var cts = new CancellationTokenSource(); // 启动WebSocket异步任务,这一步不会阻塞MainAsync的执行 var webSocketTask = RunBinanceTickersWebSocketAsync(cts.Token); // 🚀 这里可以放心写MainAsync后续的业务逻辑了,完全不会被WebSocket阻塞 Console.WriteLine("MainAsync正在执行其他异步逻辑..."); await Task.Delay(3000); // 模拟你的业务操作,比如请求其他API、处理数据等 Console.WriteLine("MainAsync的前置逻辑执行完毕,准备关闭WebSocket连接"); // 发送取消信号,通知WebSocket任务结束 cts.Cancel(); // 等待WebSocket任务完全清理资源后再继续 await webSocketTask; Console.WriteLine("程序正常退出"); } static async Task RunBinanceTickersWebSocketAsync(CancellationToken cancellationToken) { var binanceApi = new ExchangeBinanceAPI(); // 使用using确保WebSocket连接在任务结束时被正确释放 using var tickerSocket = binanceApi.GetTickersWebSocket((tickers) => { // 回调逻辑保持不变,收到tickers时打印信息 Console.WriteLine("{0} tickers received, first one: {1}", tickers.Count, tickers.First()); }); Console.WriteLine("Binance WebSocket已启动,按任意键可提前关闭;或等待MainAsync自动触发关闭"); // 把阻塞的Console.ReadKey包装成异步任务,同时绑定取消令牌 var userInputTask = Task.Run(() => Console.ReadKey(), cancellationToken); try { // 等待两个事件中的任意一个:用户按键 或 收到取消信号 await Task.WhenAny(userInputTask, Task.Delay(Timeout.Infinite, cancellationToken)); } catch (OperationCanceledException) { // 捕获取消异常,这是正常的终止流程,不需要报错 Console.WriteLine("WebSocket收到终止信号,正在断开连接..."); } } }
关键细节解释
- 取消令牌(CancellationToken):这是异步编程中优雅终止任务的标准方式,避免资源泄漏。当
MainAsync完成前置逻辑后,调用cts.Cancel()就能通知WebSocket任务安全关闭。 - 异步化用户输入:原来的
Console.ReadLine()是同步阻塞方法,会卡住整个线程。我们用Task.Run把它包装成异步任务,同时绑定取消令牌,这样既能响应用户手动关闭,又能在收到取消信号时立即终止。 - 非阻塞启动WebSocket:通过
var webSocketTask = RunBinanceTickersWebSocketAsync(cts.Token)启动任务后,MainAsync会立刻继续执行后续代码,不会等待WebSocket任务结束,完全满足你的需求。 - 资源安全释放:
using语句会自动处理WebSocket的Dispose操作,不管是用户手动关闭还是取消信号触发,都能保证连接被正确断开。
额外提示
- 如果不需要用户手动关闭WebSocket,可以去掉
userInputTask相关代码,只保留await Task.Delay(Timeout.Infinite, cancellationToken),这样WebSocket会完全由MainAsync的取消令牌控制。 - 如果你需要在
MainAsync中随时和WebSocket回调交互,可以考虑用线程安全的队列或者变量传递数据,避免多线程冲突。
内容的提问来源于stack exchange,提问作者ceds
相关产品推荐
相关产品推荐

