WPF线程详解(四)——线程

大概有4种方法:
Dispatcher、异步委托、手动多线程、BackgroundWorker,另外还有一个DispatcherTimer,是定时器。

其中Dispatcher与DispatcherTimer相同,是利用在主线程进行任务优先级的排列来模拟多线程,因此其中实质是单线程

,所以大负荷的运算不宜用它们。
BackgroundWorker会到线程池去抓一个线程作为工作线程,完成工作后切换回 UI 线程调用你定义的处理完成工作的委

托。每计算出一次值,就会用worker.ReportProgress(percentComplete)通知回调函数,并可在回调函数中更新UI。特

别适合需要更新已完成的百分比的场景。

这些都可以和数据绑定结合起来。

 

①异步委托还是手动多线程
异步委托:支持线程结束时回调(BeginInvoke方法的的第一个参数),但不支持在外部中断线程,多用于更新UI。

手动线程:支持在外部中断线程(Thread.Abort方法),但不易得到函数的返回值。另外还不能更新UI。因为“Windows

窗体”使用单线程单元 (STA) 模型(WPF窗体也一样),STA模型意味着可以在任何线程上创建窗口,但窗口一旦创建后

就不能切换线程,并且对它的所有函数调用都必须在其创建线程上发生。特别是在注册事件的回调函数中要小心。

 

委托的用法:

[c-sharp] view plain copy
  1. public class MyDelegateTest  
  2. {  
  3.     // 步骤1,声明delegate对象  
  4.     public delegate bool MyDelegate(string name);  
  5.   
  6.     // 这是我们欲传递的方法,它与MyDelegate具有相同的参数和返回值类型  
  7.     public static bool MyDelegateFunc(string name)  
  8.     {  
  9.         Console.WriteLine("Hello, {0}", name);  
  10.     return ture;  
  11.     }  
  12.   
  13.     public static voidMain ()  
  14.     {  
  15.         // 步骤2,创建delegate对象  
  16.         MyDelegate md = new MyDelegate(MyDelegateTest.MyDelegateFunc);  
  17.   
  18.         // 步骤3,调用delegate  
  19.         Console.WriteLine("Result is {0}", md("sam1111"));  
  20.     }  
  21. }  

这种调用方法和用委托对象的Invoke一样,都是同步调用,会阻塞当前线程。异步调用需要用BeginInvoke:

[c-sharp]  view plain  copy
  1. class Program   
  2. {   
  3.     private static int newTask(int ms)   
  4.     {   
  5.         Console.WriteLine("任务开始");   
  6.         Thread.Sleep(ms);   
  7.         Random random = new Random();   
  8.         int n = random.Next(10000);   
  9.         Console.WriteLine("任务完成");   
  10.         return n;   
  11.     }   
  12.   
  13.     private delegate int NewTaskDelegate(int ms);   
  14.     static void Main(string[] args)   
  15.     {   
  16.         NewTaskDelegate task = newTask;   
  17.         IAsyncResult asyncResult = task.BeginInvoke(2000, nullnull); // EndInvoke方法将被阻塞2秒   

  18.         //Do Something else  
  19.         int result = task.EndInvoke(asyncResult);   
  20.         Console.WriteLine(result);  
  21.     }  
  22. }  

这里的BeginInvoke就是异步调用,运行后立即返回,不会引起当前线程的阻塞。但是在本例程中,因为newTask中要

Sleep 2秒中,如果Do Something else的时间没有2秒的话,EndInvoke还是会引起当前线程的阻塞,因为它要等待

newTask执行完毕。

 

那能不能不调用EndInvoke,让它自己结束呢?不太好。因为一来BeginInvoke和EndInvoke必须成对调用。即使不需要返

回值,但EndInvoke还是必须调用,否则可能会造成内存泄漏,因为它是利用了线程池资源。二来往往要调用EndInvoke

来获得函数的返回值。

 

如果是用BeginInvoke来进行轮询操作,EndInvoke是无法返回的,这时可以用一个变量来控制一下:
在我的应用场景里是定义了一个包装类:

[c-sharp]  view plain  copy
  1. class IsNetworkAvailableDelegate  
  2.     {  
  3.         private IsNetworkAvailableWrapper _available;  
  4.         private delegate void MyDelegate();  
  5.         private MyDelegate _dele;  
  6.         private IAsyncResult _result;  
  7.         private bool _running = true;  
  8.   
  9.         private void TryConnect()  
  10.         {  
  11.             while (this._running)  
  12.             {  
  13.                 try  
  14.                 {  
  15.                     Ping _ping = new Ping();  
  16.                     if (_ping.Send("www.baidu.com").Status == IPStatus.Success)  
  17.                         this._available.IsNetworkAvailable = true;  
  18.                     else  
  19.                         this._available.IsNetworkAvailable = false;  
  20.   
  21.                 }  
  22.                 catch  
  23.                 {  
  24.                     this._available.IsNetworkAvailable = false;  
  25.                 }  
  26.                 Thread.Sleep(500);  
  27.             }  
  28.         }  
  29.         public IsNetworkAvailableDelegate(IsNetworkAvailableWrapper available)  
  30.         {  
  31.             this._available = available;  
  32.             this._dele = new MyDelegate(this.TryConnect);  
  33.             this._result = this._dele.BeginInvoke(nullnull);  
  34.         }  
  35.   
  36.         public void EndInvoke()  
  37.         {  
  38.             this._running = false;  
  39.             this._dele.EndInvoke(this._result);  
  40.         }  
  41.     }  

要想完全与当前线程异步,可以利用BeginInvoke的第二个参数,设置一个函数执行完成后的回调函数:

[c-sharp]  view plain  copy
  1. private delegate int MyDelegate(int a);  
  2. private int method(int a)  
  3. {  
  4.     Thread.Sleep(10000);  
  5.     Console.WriteLine(a);  
  6.     return 100;  
  7. }  
  8. private void MethodCompleted(IAsyncResult asyncResult)  
  9. {  
  10.     if (asyncResult == nullreturn;  
  11.     textBox1.Text = (asyncResult.AsyncState as MyDelegate).EndInvoke(asyncResult).ToString();  
  12. }  
  13. private void button1_Click(object sender, EventArgs e)  
  14. {  
  15.     MyDelegate my = method;  
  16.     IAsyncResult asyncResult = my.BeginInvoke(MethodCompleted, my);  
  17. }  

这样就可以让当前线程完全没有等待的感觉了。

不过有时候,当前线程又想要利用函数执行的时间干点私活,然后在函数执行完成后再做别的,可以用WaitOne方法:

[c-sharp]  view plain  copy
  1. class Program  
  2. {  
  3.   public delegate int BinaryOpDelegate(int x, int y);  
  4.   static void Main(string[] args)  
  5.   {  
  6.     Console.WriteLine("***** Async Delegate Invocation *****");  
  7.   
  8.     // Print out the ID of the executing thread.  
  9.     Console.WriteLine("Main() invoked on thread {0}.",  
  10.       Thread.CurrentThread.ManagedThreadId);   
  11.   
  12.     // Invoke Add() on a secondary thread.  
  13.     BinaryOpDelegate b = new BinaryOpDelegate(Add);  
  14.     IAsyncResult iftAR = b.BeginInvoke(10, 10, nullnull);  
  15.   
  16.     // This message will keep printing until   
  17.     // the Add() method is finished.  
  18.     //用WaitOne 等待异步委托执行完成  
  19.     while (!iftAR.AsyncWaitHandle.WaitOne(1000, true))  
  20.     {  
  21.       Console.WriteLine("Doing more work in Main()!");  
  22.     }  
  23.   
  24.     // Obtain the result of the Add()   
  25.     // method when ready.  
  26.     int answer = b.EndInvoke(iftAR);  
  27.     Console.WriteLine("10 + 10 is {0}.", answer);  
  28.     Console.ReadLine();  
  29.   }  
  30.  
  31.   #region Very slow addition...  
  32.   static int Add(int x, int y)  
  33.   {  
  34.     // Print out the ID of the executing thread.  
  35.     Console.WriteLine("Add() invoked on thread {0}.",  
  36.       Thread.CurrentThread.ManagedThreadId);   
  37. 模拟一个花费时间较长的行为  
  38.     // Pause to simulate a lengthy operation.  
  39.     Thread.Sleep(5000);  
  40.     return x + y;  
  41.   }    
  42.   #endregion  
  43. }  

PS:用lambda语法还可以写出很炫的句子:)

[c-sharp]  view plain  copy
  1. Action<object> action=(obj)=>method(obj);  
  2. action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null);  

其实还可以更直接:

[c-sharp]  view plain  copy
  1. new Action<object>((obj) => method(obj)).BeginInvoke(ar=>action.EndInvoke(ar), null);  

Invoke方法的主要功能就是帮助你在UI线程上调用委托所指定的方法。Invoke方法首先检查发出调用的线程(即当前线程

)是不是UI线程,如果是,直接执行委托指向的方法,如果不是,它将切换到UI线程,然后执行委托指向的方法。

 

手动线程的调用:
典型的写法是:

[c-sharp]  view plain  copy
  1. public class ProcClass  
  2. {  
  3.      private string procParameter = "";  
  4.      private string result = "";  
  5.      public ProcClass(string parameter)  
  6.      {  
  7.          procParameter = parameter;  
  8.      }  
  9.      public void ThreadProc()  
  10.      {  
  11.      }  
  12. }  
  13. ProcClass threadProc = new ProcClass("use thread class");  
  14. Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );  
  15. thread.IsBackground = true;  
  16. thread.Start();  

因为Thread调用的函数只能是无参数且无返回值的,因此通常要用类进行包装。

 

如果线程既需要可中断,又要与UI打交道:

[c-sharp]  view plain  copy
  1. public class ProcClass  
  2. {  
  3.      private string procParameter = "";  
  4.      private Form1.OutDelegate delg = null;  
  5.      public ProcClass(string parameter, Form1.OutDelegate delg)  
  6.      {  
  7.          procParameter = parameter;  
  8.          this.delg = delg;  
  9.      }  
  10.      public void ThreadProc()  
  11.      {  
  12.          delg.BeginInvoke("use ProcClass.ThreadProc()"nullnull);  
  13.      }  
  14. }  
  15. ProcClass threadProc = new ProcClass("use thread class"new OutDelegate(OutText));  
  16. Thread thread = new Thread( new ThreadStart( threadProc.ThreadProc ) );  
  17. thread.IsBackground = true;  
  18. thread.Start();  


猜你喜欢

转载自blog.csdn.net/yinweimumu/article/details/80582139