BackgroundWorker.ProgressChanged 事件不在调度程序线程上
本文关键字:调度程序 线程 ProgressChanged 事件 BackgroundWorker | 更新日期: 2023-09-27 18:30:41
我有以下代码,它从后台工作人员更新我的进度条和状态栏。 我运行同一个后台工作线程两次。 第一次运行它时,我从 MainWindow 构造函数调用它,它工作正常。 在构造函数结束时,我设置了一个计时器来经常调用该方法。
System.Threading.TimerCallback timerCallback = new System.Threading.TimerCallback(RefreshWebDataTimer);
timer = new System.Threading.Timer(
timerCallback, null,
Dictionary.MS_TIMER_FIRSTREFRESH_PERIOD,
Dictionary.MS_TIMER_REFRESH_PERIOD);
从计时器调用它时,出现以下错误:
类型为"System.InvalidOperationException"的第一次机会异常 发生在 WindowsBase 中.dll 其他信息:调用线程 无法访问此对象,因为其他线程拥有它。
我添加了一些调试,确实调度程序线程与计时器位于不同的线程上,并且与原始运行位于同一线程上。
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
System.Diagnostics.Debug.Print("Current Thread: {0}", System.Threading.Thread.CurrentThread.ManagedThreadId);
System.Diagnostics.Debug.Print("Dispatcher Thread: {0}", progressBar.Dispatcher.Thread.ManagedThreadId);
this.progressBar.Visibility = Visibility.Visible;
this.progressBar.Value = e.ProgressPercentage;
if (e.UserState != null)
{
this.statusBar.Text = e.UserState.ToString();
}
}
当前线程: 22 调度程序线程:7
我的印象是,ProgressChanged
和RunWorkerCompleted
事件总是在主 UI 线程上运行,以便解决此问题并能够进行 UI 更新。 显然,我误解了这里发生的事情。
我更新了我的解决方案以使用调度程序,如下所示:
private void backgroundWorker_ProgressChanged(object sender,
ProgressChangedEventArgs e)
{
progressBar.Dispatcher.BeginInvoke(new OneArgIntDelegate(updateProgressBar), e.ProgressPercentage);
if (e.UserState != null)
{
progressBar.Dispatcher.BeginInvoke(new OneArgStrDelegate(updateStatusBar), e.UserState.ToString());
}
}
private void updateStatusBar(string Text)
{
this.statusBar.Text = Text;
}
private void updateProgressBar(int ProgressPercentage)
{
this.progressBar.Visibility = Visibility.Visible;
this.progressBar.Value = ProgressPercentage;
}
这个解决方案有效,但我认为 BackgroundWorker 的全部意义在于我不必这样做。有人可以解释我的错误假设以及真正发生的事情。有没有办法通过以不同的方式设置计时器来在没有调度程序的情况下做到这一点?
谢谢
哈里森
我的印象是进度发生了变化和 RunWorker已完成事件始终在主 UI 线程上运行,以便 解决此问题并能够进行UI更新。显然,我 误解了这里发生了什么。
BackgroundWorkers ProgressChanged 被回调到拥有 BackroundWorker 的线程,这不会离开 UI 线程,当您第二次在另一个线程上创建BackgroundWorker
时,将在创建BackgroundWorker
的线程上调用ProgressChanged
在本例中为计时器线程。
- 第一次调用: UIThread => BacgroundWorker => 进度 => UIThread
- 第二次调用:计时器线程 => BacgroundWorker =>进度 => 计时器线程
可以将RefreshWebDataTimer
从Timer
以太调用到 UI 线程,也可以使用DispatcherTimer
来确保在 UI 线程上调用RefreshWebDataTimer
。
选项 1:
timer = new System.Threading.Timer(
(o) => Dispatcher.Invoke((Action)RefreshWebDataTimer),
null,
Dictionary.MS_TIMER_FIRSTREFRESH_PERIOD,
Dictionary.MS_TIMER_REFRESH_PERIOD);
选项 2:
timer = new DispatcherTimer(
TimeSpan.FromSeconds(1),
DispatcherPriority.Background,
(s, e) => RefreshWebDataTimer(),
Dispatcher);