我在UI(WPF)线程中创建了BackgroundWorker(mainBw
)。它有一个无限循环,它休眠1.5秒并通过Application.Current.Dispatcher.Invoke
调用一个函数,它只是将“全局”text
变量中的文本输出到TextBox。
同样在循环之前,它创建了另一个(child
)BackgroundWorker,其中ReportsProgress在ProgressChanged事件处理程序中修改了text
变量。
我认为它不起作用,因为在mainBw
循环中没有像WinForms Application.DoEvents()这样的东西,所以它无法处理事件处理程序。但它的确有效。为什么呢?
以下是代码:
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows;
namespace WpfApplication6
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private BackgroundWorker mainBw = new BackgroundWorker();
private void Button_Click(object sender, RoutedEventArgs e)
{
mainBw.DoWork += MainBwOnDoWork;
mainBw.RunWorkerAsync();
btn.IsEnabled = false;
}
private string text = "abc";
private void MainBwOnDoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += BwOnDoWork;
bw.ProgressChanged += BwOnProgressChanged;
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync();
while (true)
{
Thread.Sleep(1500);
text += " main ";
Application.Current.Dispatcher.Invoke(new Action(() => { WriteToUIThread(); }));
}
}
private void WriteToUIThread()
{
tbox.Text = DateTime.Now + " " + text + Environment.NewLine + tbox.Text;
}
private void BwOnProgressChanged(object sender, ProgressChangedEventArgs e)
{
text += e.UserState.ToString();
}
private void BwOnDoWork(object sender, DoWorkEventArgs doWorkEventArgs)
{
while (true)
{
Thread.Sleep(3000);
(sender as BackgroundWorker).ReportProgress(0, "child");
}
}
}
}
// XAML
<Window x:Class="WpfApplication6.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Width="525">
<Grid>
<Button Name="btn" Content="Button" HorizontalAlignment="Left" Height="105" Margin="43,47,0,0" VerticalAlignment="Top" Width="165" Click="Button_Click"/>
<TextBox Name="tbox" HorizontalAlignment="Left" Height="114" TextWrapping="Wrap" Text="TextBox" VerticalAlignment="Top" Width="456" Margin="27,182,0,0"/>
</Grid>
</Window>
答案 0 :(得分:2)
BackgroundWorker使用通用方法获取在UI线程上运行的代码,它使用静态SynchronizationContext.Current属性来查找同步提供程序。 ReportProgress()使用其Post()方法封送调用。
如果您运行Winforms应用程序,则Current属性将引用WindowsFormsSynchronizationContext class的实例。在创建表单或调用Application.Run()时自动安装。它使用Control.Begin / Invoke()来实现Post和Send方法。
如果您运行WPF应用程序,那么Current属性将引用DispatcherSynchronizationContext的实例,它使用Dispatcher.Begin / Invoke()。
所以这只是自动生效。
答案 1 :(得分:1)
它的工作原理是因为BackgroundWorker
在后台线程中起作用(因此名称)。因为它没有在UI线程中运行,所以它不会阻塞UI线程,它只是偶尔发送短的方法在UI线程中运行。
尽管如此,它仍然不是解决问题的特别好的方法。如果您想每3秒运行一些代码,请使用Timer
代替。如果您在Forms
命名空间中使用计时器,它将代表您在UI线程中触发它的事件。
答案 2 :(得分:0)
它有效,因为BackgroundWorker
运行 - 正如其名称所示 - 在后台线程中独立于主UI线程运行。
ReportProgress
- 事件被编组到UI线程,以便可以轻松处理此事件,而无需调用Invoke
- 所涉及的控件上的方法。
Contraray,WinForms中的Application.DoEvents()
- 方法允许进程处理其他消息,以便在主线程中执行长时间操作,不使用使用后台线程。