ASP.NET网站中CancellationToken取消长任务失效问题求助
问题:ASP.NET Web Forms中async/await配合CancellationToken无法实时取消任务
我在ASP.NET网站应用里尝试用async/await结合CancellationToken实现长任务取消,但点击「Cancel」按钮后,取消事件要等长任务执行完毕才触发,完全达不到实时取消的预期效果。
场景包含两个按钮:
- btnStart:启动长时任务
- btnCancel:触发任务取消
我的代码如下:
Private Const TokenKey As String = "MyOperationToken" Protected Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click ' Create and store the cancellation token source in Session Dim cts As New CancellationTokenSource() Session(TokenKey) = cts Try lblResult.Text = "Operation started..." Dim result As String = Await SimulateLongOperationAsync(cts.Token) lblResult.Text = "Completed: " & result Catch ex As OperationCanceledException lblResult.Text = "Operation was cancelled." Catch ex As Exception lblResult.Text = "Error: " & ex.Message Finally Session.Remove(TokenKey) End Try End Sub Protected Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click ' Retrieve and cancel the token source if it exists Dim cts As CancellationTokenSource = TryCast(Session(TokenKey), CancellationTokenSource) If cts IsNot Nothing Then cts.Cancel() lblResult.Text = "Cancellation requested." Else lblResult.Text = "No operation to cancel." End If End Sub Private Async Function SimulateLongOperationAsync(token As CancellationToken) As Task(Of String) ' Simulate 10 seconds of work For i As Integer = 1 To 10 token.ThrowIfCancellationRequested() Await Task.Delay(1000, token) Next Return "Success after 10 seconds" End Function
调试时发现,点击「Start」后长任务运行,此时点击「Cancel」,btnCancel_Click的代码要等长任务结束才会执行,请问问题出在哪?
解答
核心问题是ASP.NET Web Forms的Session独占锁机制:当btnStart_Click中读写Session时,ASP.NET会对当前用户的Session加独占锁,且该锁会持有到整个请求处理完成(包括Await之后的代码执行完毕)。后续btnCancel_Click请求因为要访问同一个Session,会被强制阻塞,直到第一个请求释放Session锁,这就导致取消操作无法实时触发。
解决方法:替换Session存储方式
将CancellationTokenSource从Session转移到HttpRuntime.Cache中,避免后续请求因访问Session被阻塞。修改后的代码如下:
Private Function GetTokenCacheKey() As String ' 用SessionID作为缓存键,确保每个用户的任务独立 Return $"MyOperationToken_{Session.SessionID}" End Function Protected Async Sub btnStart_Click(sender As Object, e As EventArgs) Handles btnStart.Click Dim cts As New CancellationTokenSource() ' 将CancellationTokenSource存入Cache,设置10分钟过期避免内存泄漏 HttpRuntime.Cache.Insert(GetTokenCacheKey(), cts, Nothing, DateTime.Now.AddMinutes(10), TimeSpan.Zero) Try lblResult.Text = "Operation started..." Dim result As String = Await SimulateLongOperationAsync(cts.Token) lblResult.Text = "Completed: " & result Catch ex As OperationCanceledException lblResult.Text = "Operation was cancelled." Catch ex As Exception lblResult.Text = "Error: " & ex.Message Finally ' 任务结束后移除缓存 HttpRuntime.Cache.Remove(GetTokenCacheKey()) End Try End Sub Protected Sub btnCancel_Click(sender As Object, e As EventArgs) Handles btnCancel.Click ' 从Cache获取CancellationTokenSource Dim cts As CancellationTokenSource = TryCast(HttpRuntime.Cache.Get(GetTokenCacheKey()), CancellationTokenSource) If cts IsNot Nothing Then cts.Cancel() lblResult.Text = "Cancellation requested." Else lblResult.Text = "No operation to cancel." End If End Sub Private Async Function SimulateLongOperationAsync(token As CancellationToken) As Task(Of String) ' Simulate 10 seconds of work For i As Integer = 1 To 10 token.ThrowIfCancellationRequested() Await Task.Delay(1000, token) Next Return "Success after 10 seconds" End Function
额外说明
- 单服务器场景下,
HttpRuntime.Cache足够满足需求;如果是多服务器集群环境,可改用分布式缓存(如Redis)来跨节点共享CancellationTokenSource的引用。 - 给缓存设置合理的过期时间,防止因任务异常结束导致内存泄漏。
内容的提问来源于stack exchange,提问作者WhatsInAName
相关产品推荐
相关产品推荐

