如何在 .NET 中取消 Task.Wait() 之后正在运行的 OpenAsync()?
Posted
技术标签:
【中文标题】如何在 .NET 中取消 Task.Wait() 之后正在运行的 OpenAsync()?【英文标题】:How can I cancel a running OpenAsync() After Task.Wait() in .NET? 【发布时间】:2021-07-05 21:34:31 【问题描述】:我想随时通过客户端单击按钮连接到数据库。当客户端使用不同的 ConnectionString 多次单击按钮时。现在我只想返回客户端创建的最后一个连接。因此,我必须取消最后一个任务并创建新任务。问题是如何在调用 Task.wait() 使用 CancellationToken 后取消任务?因为当我还没有调用wait()
时,任务状态会是WaitForActive,所以我永远无法收到结果。请帮我解决这个案子。我的代码草稿如下:
private SqlConnection _connection = null;
private CancellationTokenSource _source = null;
private Task<string> _result = null;
public Task<string> GetStatusAsync(string connectionString)
if (_source == null)
_source = new CancellationTokenSource();
if (_connection != null && _connection.State == ConnectionState.Connecting)
_source.Cancel();
_source = new CancellationTokenSource();
_result = ConnectDatabaseAsync(connectionString.Trim(),_source.Token);
_result.Wait();
return _result;
private async Task<string> ConnectDatabaseAsync(string connectionString, CancellationToken cancellationToken)
if (_connection == null)
_connection = new SqlConnection();
try
_connection.ConnectionString = connectionString;
await _connection.OpenAsync(cancellationToken);
return "ok";
catch (Exception ex)
return ex.Message;
finally
if (_connection.State == ConnectionState.Open)
_connection.Close();
【问题讨论】:
你不应该使用_result.Wait()
- 这会阻塞线程,因此用户将永远无法点击按钮 - 你的整个应用程序被冻结
@torvin 谢谢。我知道。 await 任务将与 Wait() 相同,因为当调用 await 或 wait() 时,我的任务将永远不会启动。
您使用的是 WinForm 还是 Web 应用程序?
@Saeed Winform。你有什么想法吗?
【参考方案1】:
不要因为冻结主线程而对任务使用等待。
您可以像这样使用任务字典和取消令牌来管理您的任务(我只是模拟您的方法):
private static readonly Dictionary<Task, CancellationTokenSource> Tasks = new Dictionary<Task, CancellationTokenSource>();
public async Task<string> GetStatusAsync()
var source = new CancellationTokenSource();
var task = ConnectDatabaseAsync("YourConnection", source.Token);
if (Tasks.Any())
foreach (var item in Tasks)
if (!item.Key.IsCompleted && !item.Value.IsCancellationRequested)
item.Value.Cancel();
Tasks.Add(task, source);
await task;
return task.Result;
private async Task<string> ConnectDatabaseAsync(string connection, CancellationToken token)
await Task.Delay(10000, token);
return "Ok";
【讨论】:
我可以看到你有一个条件是!item.Key.IsCompleted && !item.Value.IsCancellationRequested
并调用cancel()
方法取消。那么,Cancel() 方法兄弟在哪里?如果 !item.Value.IsCancellationRequested 为真,也许我不想杀死最后一个任务。
@StevenTrinh 使用火灾取消令牌,您可以结束任务。当您从 CancellationTokenSource
调用 Cancel
方法时,您会取消该任务并且不需要额外的取消方法。对于那个If
,当item.Key.IsCompleted
为真时意味着你的任务已经完成并且不能取消它,当item.Value.IsCancellationRequested
为真时意味着任务已经被取消,不需要取消它。
@StevenTrinh 你测试过这个答案吗?没事吧?
已经检查过了。这里有问题。那是 OpenAsync() 运行的时候。它将首先检查 CancellationToken 然后调用 Open() 方法进行连接。所以,稍后,我调用 Cancel() 但它不会取消 OpenAsync() 因为它已经退出了检查 canceltoken 部分。
@StevenTrinh 否,如果取消令牌在运行任务期间触发,它将取消任务。你的问题是在新的传入任务之前取消队列中的所有任务,我认为这个解决方案会这样做。以上是关于如何在 .NET 中取消 Task.Wait() 之后正在运行的 OpenAsync()?的主要内容,如果未能解决你的问题,请参考以下文章
在 C# 中使用 Web 服务:删除 Task.Wait()