【发布时间】:2022-03-28 19:32:27
【问题描述】:
我正在开发一个网络应用程序,该应用程序从 websocket 接收数据,对其进行修改,并将其上传到数据服务。上传数据需要一些时间,我想隐藏这种延迟,一次上传多条消息。上传完成后,我需要通过 websocket 发回确认。
我创建了一个工作队列来保存所有未完成的任务。这似乎运作良好,所以我没有包括它。但是我的上传任务似乎在它实际完成之前就完成了。这是一个精简的示例。
private async Task UploadDataAsync(string data, CancellationToken cancellationToken)
{
Task uploadTask = new Task(async () =>
{
// Simulates uploading data
await Task.Delay(5000, cancellationToken);
});
_ = uploadTask.ContinueWith(async (t1) =>
{
// Clean up the task
await RemoveTask(t1);
if (t1.IsCompletedSuccessfully)
await SendAck(this, data, cancellationToken);
else if (t1.IsFaulted)
logger.LogError(t1.Exception, $"An error occurred while uploading {data}");
else if (t1.IsCanceled)
logger.LogWarning($"An upload task {data} was canceled");
}, TaskScheduler.Default);
await AddTask(uploadTask);
uploadTask.Start();
}
在前面的代码中,确认是在数据上传之前发送的。令人惊讶的是,这似乎是因为我的 Task 使用了异步 lambda。出于某种原因,我不明白 uploadTask 在等待上传时完成。所以我把它改成了这样:
private async Task UploadDataAsync(string data, CancellationToken cancellationToken)
{
Task uploadTask = new Task(() =>
{
// Simulates uploading data
Task.Delay(5000, cancellationToken).Wait();
});
_ = uploadTask.ContinueWith((t1) =>
{
// Clean up the task
RemoveTask(t1).Wait();
if (t1.IsCompletedSuccessfully)
SendAck(this, data, cancellationToken).Wait();
else if (t1.IsFaulted)
logger.LogError(t1.Exception, $"An error occurred while uploading {data}");
else if (t1.IsCanceled)
logger.LogWarning($"An upload task {data} was canceled");
}, TaskScheduler.Default);
await AddTask(uploadTask);
uploadTask.Start();
}
现在一切都以正确的顺序执行,除非出现问题或操作被取消(例如服务器关闭)。现在我正在处理 AggregateExceptions 和 TaskCanceledExceptions。
这看起来应该比我做的更容易。我做错了吗?
编辑添加调用UploadDataAsync作为上下文的伪代码。
protected override async Task DoConnection(CancellationToken cancellationToken)
{
while (_websocket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)
{
// Simulates getting websocket data
string result = await _websocket.ReceiveAsync();
// This should only asynchronously wait for the upload task to get
// created. It should not wait for the upload to complete.
await OnDataReceived(result, cancellationToken);
}
}
【问题讨论】:
-
你在哪里等待
uploadTask?你在AddTask做什么? -
在第一个代码的末尾添加 uploadTask.Wait()。
-
@HirasawaYui Don't block on async code
-
@HirasawaYui 一般来说,在 C# 中混合使用
await和.Wait()是个坏主意 - 您应该使用其中一个。否则,在大多数情况下它会死锁(请参阅链接文章)。 -
"实例化一个Task对象并启动一个任务的最常用方法是调用静态
Task.Run(Action)或TaskFactory.StartNew(Action)方法,而不是调用这个构造函数。用于需要任务的创建和启动分离的高级场景。” Link
标签: c# async-await