public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private CancellationTokenSource cts;
private void Calculate(int i)
{
Math.Pow(i, i);
}
public void DoWork(IProgress<int> progress, CancellationToken cancelToken)
{
for (int j = 0; j < 100000; j++)
{
if (cancelToken.IsCancellationRequested)
cancelToken.ThrowIfCancellationRequested();
Calculate(j);
progress?.Report((1 + j) * 100 / 100000);
}
}
private async void run_Click(object sender, EventArgs e)
{
cts = new CancellationTokenSource();
var cancelToken = cts.Token;
progressBar.Maximum = 100;
progressBar.Step = 1;
var progress = new Progress<int>(v => progressBar.Value = v);
try
{
await Task.Run(() => { DoWork(progress, cancelToken); }, cts.Token);
}
catch (OperationCanceledException ex)
{
Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {ex.Message}");
}
finally
{
cts = null;
}
}
private void cancel_Click(object sender, EventArgs e)
{
Console.WriteLine("Cancel");
cts?.Cancel();
}
}
点击运行后,UI冻结,我无法点击取消按钮。我已经阅读了以下博客:
- https://blog.stephencleary.com/2012/07/dont-block-on-async-code.html(请勿在异步代码上阻塞)
- Accessing UI controls in Task.Run with async/await on WinForms(使用async/await在WinForms的Task.Run中访问UI控件)
- https://devblogs.microsoft.com/csharpfaq/parallel-programming-task-cancellation/(并行编程任务取消)
- https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/async/fine-tuning-your-async-application(微调异步应用程序)
await Task.Factory.StartNew(() => { DoWork(progress, cancelToken); }, cts.Token);
这个操作不起作用,无法在加载期间点击取消按钮。有什么想法吗?(我确定这非常简单)。
DoWork
中添加类似于await Task.Delay(TimeSpan)
的内容。 - JStewardReport
时切换到此同步上下文。因此,对于非常紧密的循环(比如这里),您不断地切换到 UI 线程,并且考虑到更新进度比您的Compute
函数更加计算密集,您的DoWork
方法大多数时间都在 UI 线程上运行并阻塞它。您应该只报告每1000个项目左右。 - ckuriprogress?.Report((1 + j) * 100 / 100000000);
前面添加了if (j % 500 == 0)
,并将循环增加到了 100000000,现在它完美地工作了。代码实际上是正确的,正如你所说的,DoWork
没有做足够的工作。 - Hydraxize