WPF 异步编程
DispatherObject
很多wpf 对象有线程相关性(thread affinity),意味在你只能在创建它的线程上使用它。这和window form的UI控件一样。 WinForm的control.invoke() ,winform.timer 会在进行UI操作时做一个线程切换。
在wpf中,继承自System.Windows.Threading的DispatherObject的对象都具有线程相关性, 比如:Brush, Geometry
public class DependencyObject : DispatcherObject
public abstract class DispatcherObject
{
// Fields
private Dispatcher _dispatcher;
// Methods
protected DispatcherObject();
[EditorBrowsable(EditorBrowsableState.Never)]
public bool CheckAccess();
[FriendAccessAllowed]
internal void DetachFromDispatcher();
[EditorBrowsable(EditorBrowsableState.Never)]
public void VerifyAccess();
// Properties
[EditorBrowsable(EditorBrowsableState.Advanced)]
public Dispatcher Dispatcher { get; }
}
CheckAccess()与VeriftyAccess()来判断是否在正确的线程上,VerifyAccess会抛异常,如果不在正确的线程上。Freezable对象能够通过frozon,来消除线程的限制。
The Dispather:
每一个需要创建UI objects的线程必须要有一个dispatcher object.这个对象做的事相当于win32的消息泵,用一个循环不断dispatch input message to appropriate handlers.
DispatcherObject中有这个属性,或者可以通过Dispatcher.CurrentDispather来获得当前线程的dispather.
如果要做完一些事情后更新UI,必须保证在UI线程上更新。Dispatcher 就是提供这种功能的,Dispatcher.Invoke()方法 会等待方法执行完再返回,Dispather.Invoke()方法不会等待方法执行完,而是立即返回。Invoke()方法有死锁风险,BeginInvoke 没有。
匿名方法:
Public class window1:window
{
Public void delegate Mymethod;
Mymethod method = delegate{ this.foreground = new SolidColorBrush(Color.Blue);}
This.Dispather.BeginInvoke(DispatcherPriority.Normal,method);
} //有ApplicationIdle 和SystemIdle优先级
;
}
传参数的方法:delegate void BackGround(Color c)
Private void SetBackgroundColor(Color c) {this.background = c;}
BackGround method = SetBackgroundColor;
DispatcherOperation e =This.dispacher.BeginInvoke(DispatherPriority.Normal,method,Color.Blue);
返回的 DispatherOperation.Status属性可以查看状态:
DispatcherOperationStatus
Value |
Meaning |
Pending
|
The dispatcher has not yet called the method. |
Executing
|
The method is currently executing on the dispatcher thread. |
Completed
|
The method has finished executing. |
Aborted
|
The operation was aborted. |
可以用DispatherOperation.Abort()退出,不要DispatherOperation.Completed+=delegate{}; 因为当加上的时候可能线程可能已经返回了。直接把完成时要做的事,放到线程执行方法中去,特别是界面处理。
没有了.net异步编程的,EndInvoke()方法,也没有把一个completion callback传给BeginInvoke的 方式。
DispatcherTimer:
WPF 的timer,
private DispatcherTimer dt;
public MyWindow( ) {
DispatcherTimer dt = new DispatcherTimer( );
dt.Tick += dt_Tick;
dt.Interval = TimeSpan.FromSeconds(2);
dt.Start( );
}
void dt_Tick(object sender, EventArgs e) {
Random r = new Random( );
byte[] vals = new byte[3];
r.NextBytes(vals);
Color c = Color.FromRgb(vals[0], vals[1], vals[2]);
this.Background = new SolidColorBrush(c);
}
private DispatcherTimer _timer;
timer = new DispatcherTimer(DispatcherPriority.Background);
timer.Interval = TimeSpan.FromMinutes(5);
timer.Tick += delegate { ScheduleUpdate(); };
timer.Start();
多UI thread和dispather
一个Application能有多个线程来创建UI,但是一个窗口和它的element都必须在一个线程中,每一个host UI object的线程都需要一个dispatcher,让UI 对象来工作。在一个单线程的application,我们不需要创建dispather,因为application类会自动在startup时创建dispatcher,在Exit时候shut down dispatcher.但是如果我们要自己建多线程UI,就得自己start up和shut down Dispather.
ThreadStart threadMethod = delegate {
Window1 w = new Window1( );
w.Show( );
System.Windows.Threading.Dispatcher.Run( ); // Won't return until dispatcher shuts down
};
Thread thread = new Thread(threadMethod);
thread.SetApartmentState(ApartmentState.STA);
thread.Start( );
继承DispatcherObject基类会在创建时自动建dispatcher实例,所以不需要new,但是要Dispatcher.Run()来使得message能够正确发送到UI object的线程中。这个方法不会返回知道dispatcher.InvokeShutdown. 对于客户自己创建的UI线程,需要手动shut down。
set the COM threading model to STA线程套间,虽然在com交互时候才用到COM threading model, 但是因为太多的系统特征需要com交互,所以dispatcher要求设置STA。
BackgroundWorker:
System.ComponentModel.BackgroundWorker, 这个类可以在windows forms和WPF中用,但是却是两种不同的实现机制。用AsyncOperationManager来区分,一个application层的变量,当WPF application start up时候,WPF会自动配置AsyncOperationManager,(用当前dispatcher).
WPF的BackgroundWorker实际上是为了更方便使用,对dispatcher的包装。
使用:
1. Attach event to DoWork event.
2. Add handler to ProgressChanged, RunWorkerCompleted event
3. 为了使用progressChanged event,要设置BackgroundWorker.WorkerReportsProgress 为true; 在DoWork的handler中,调ReportProgress()报告状态。
例子:
partial class MyWindow : Window {
private BackgroundWorker bw;
public MyWindow( ) {
bw = new BackgroundWorker( );
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(
bw_RunWorkerCompleted);
bw.WorkerReportsProgress = true;
bw.RunWorkerAsync( );
}
void bw_DoWork(object sender, DoWorkEventArgs e) {
// Running on a worker thread
for (int i = 0; i < 10; ++i) {
int percent = i * 10;
bw.ReportProgress(percent);
Thread.Sleep(1000);
}
}
void bw_ProgressChanged(object sender, ProgressChangedEventArgs e) {
this.Text = "Working: " + e.ProgressPercentage + "%";
}
void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) {
this.Text = "Finished";
}
当运行RunWorkerAsync( ),BackgroundWorker 在worker thread中抛出DoWork event,所以在DoWork handler中不能进行UI操作。但是ProgressChanged and RunWorkerCompleted events 是在UI thread中raise的,在它们的handler中可以进行UI 操作。
RunWorkerCompleted handler 传入RunWorkerCompletedEventArgs object,DoWork m方法中可能抛出异常,如果有异常抛出,RunWorkerCompletedEventArgs 对象会包含异常,否则为null。