WPF 异步编程

 

DispatherObject

很多wpf 对象有线程相关性(thread affinity),意味在你只能在创建它的线程上使用它。这和window formUI控件一样。 WinFormcontrol.invoke() ,winform.timer 会在进行UI操作时做一个线程切换。

 

wpf中,继承自System.Windows.ThreadingDispatherObject的对象都具有线程相关性, 比如: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 threaddispather

 

一个Application能有多个线程来创建UI,但是一个窗口和它的element都必须在一个线程中,每一个host UI object的线程都需要一个dispatcher,让UI 对象来工作。在一个单线程的application,我们不需要创建dispather,因为application类会自动在startup时创建dispatcher,Exit时候shut down dispatcher.但是如果我们要自己建多线程UI,就得自己start upshut 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 formsWPF中用,但是却是两种不同的实现机制。用AsyncOperationManager来区分,一个application层的变量,当WPF application start up时候,WPF会自动配置AsyncOperationManager,(用当前dispatcher).

 

WPFBackgroundWorker实际上是为了更方便使用,对dispatcher的包装。

使用:

1.      Attach event to DoWork event.

2.      Add handler to ProgressChanged, RunWorkerCompleted event

3.      为了使用progressChanged event,要设置BackgroundWorker.WorkerReportsProgress true; DoWorkhandler中,调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 threadraise的,在它们的handler中可以进行UI 操作。

 

RunWorkerCompleted handler 传入RunWorkerCompletedEventArgs objectDoWork m方法中可能抛出异常,如果有异常抛出,RunWorkerCompletedEventArgs 对象会包含异常,否则为null。

 

posted on 2008-10-30 00:59  oyl  阅读(1466)  评论(0编辑  收藏  举报

导航