c# - 在 BackgroundWorker 的 RunWorkerCompleted 中运行 Async/Await 任务
问题描述
我有一个BackgroundWorker
运行生成大量文本的作业。
完成后,我需要它来执行一个Async/Await
Task
方法,该方法在RichTextBox
.
这Async/Await
Task
是为了防止MainWindow
UI 线程在计算工作(例如搜索和着色)时冻结RichTextBox
。
错误
例外:“调用线程无法访问此对象,因为不同的线程拥有它。”
除非我将Async/Await
代码放入Dispatcher.Invoke
.
但是使用 aDispatcher.Invoke
似乎会否定Async/Await
并导致MainWindow
UI 线程冻结。
C#
public void Generate()
{
// Background Worker
//
BackgroundWorker bw = new BackgroundWorker();
bw.WorkerSupportsCancellation = true;
bw.WorkerReportsProgress = true;
bw.DoWork += new DoWorkEventHandler(delegate (object o, DoWorkEventArgs args)
{
BackgroundWorker b = o as BackgroundWorker;
// Generate some text
// ...
});
// When Background Worker Completes Job
//
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(delegate (object o, RunWorkerCompletedEventArgs args)
{
// Write and Colorize text in RichTextBox
Task<int> task = Display();
bw.CancelAsync();
bw.Dispose();
});
bw.RunWorkerAsync();
}
// Method that Writes and Colorizes text in RichTextBox in MainWindow UI
//
public async Task<int> Display()
{
int count = 0;
await Task.Run(() =>
{
// Problem here, it will only work inside a Disptacher
//Dispatcher.Invoke(new Action(delegate {
// Write text
Paragraph paragraph = new Paragraph();
richTextBox1.Document = new FlowDocument(paragraph);
richTextBox1.BeginChange();
paragraph.Inlines.Add(new Run("Test"));
richTextBox1.EndChange();
// Colorize Text here...
// Is a loop that takes some time.
// MainWindow UI freezes until it's complete.
//}));
});
return count;
}
解决方案
我同意其他人的观点,即一旦您将代码替换BackgroundWorker
为Task.Run
. 除此之外,它更容易组合await
(通过“组合”,我的意思是“做这件事;然后做另一件事”)。请注意,您应该使用await
而不是ContinueWith
.
因此,一旦 BGW 转换为,您的原始代码最终会看起来像这样Task.Run
:
public string GenerateSomeText(CancellationToken token)
{
// Generate some text
}
public async Task GenerateAsync()
{
var cts = new CancellationTokenSource();
var result = await Task.Run(() => GenerateSomeText(cts.Token));
await DisplayAsync(result);
}
那么,首先提出这个问题的问题:如何在不阻塞 UI 的情况下做大量的 UI 工作?好吧,没有一个很好的解决方案,因为工作是UI 工作。所以它不能放在后台线程上。如果你有大量的 UI 工作要做,唯一真正的选择是:
- 虚拟化您的数据。这样,您只需要处理您正在显示的 UI 数量。这是解决此问题的最佳方法。
- 如果您不想投入工作来虚拟化您的数据,那么您可以投入一个 hack,您的代码会定期暂停 UI 工作,以便 UI 保持响应。
我不相信 WPFRichTextBox
支持虚拟化,所以你可能需要去第三方。如果您想改用 pause hack,则可以执行以下操作:
public async Task<int> DisplayAsync(string text)
{
int count = 0;
// Write text
Paragraph paragraph = new Paragraph();
richTextBox1.Document = new FlowDocument(paragraph);
richTextBox1.BeginChange();
paragraph.Inlines.Add(new Run(text));
richTextBox1.EndChange();
// Colorize Text here...
// Is a loop that takes some time.
for (loop)
{
... // Colorize piece of text.
await Task.Delay(TimeSpan.FromMilliseconds(20));
}
return count;
}
推荐阅读
- jhipster - Vue 警报消息处理
- c++ - 我可以从 wxWidget wxTextCtrl 中读取 åäö 吗?
- c# - ASP.NET Core 5 MVC - 带有选项的选择元素中的会话存储
- video - 如何一起加入 amp-ima-video 和 amp-video-iframe?
- javascript - 在打开的网页上用 Python 运行 JavaScript JS 代码
- javascript - 没有“new”就不能调用“x”
- python - 保存编辑后用户的最后编辑名称
- python - 无论输入如何,Tensorflow 模型每次都保持预测接近相同的值
- javascript - Why does my website shift to the left on Safari?
- macos - macOS 动态链接器加载不需要的 dylib