C#绝对新手之C#中的多线程小结

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

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

,所以大负荷的运算不宜用它们。在异步调用的时候,使用Dispatcher.Invoke(addItem, image);


BackgroundWorker会到线程池去抓一个线程作为工作线程,完成工作后切换回 UI 线程调用你定义的处理完成工作的委

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

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

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

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

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

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

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

委托的用法:

[c-sharp] view plaincopy

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

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

[c-sharp] view plaincopy

  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. private delegate int NewTaskDelegate(int ms);  
  13. static void Main(string[] args)  
  14.     {  
  15.         NewTaskDelegate task = newTask;  
  16.         IAsyncResult asyncResult = task.BeginInvoke(2000, null, null); // EndInvoke方法将被阻塞2
  17. 秒  
  18. //Do Something else
  19. int result = task.EndInvoke(asyncResult);  
  20.         Console.WriteLine(result); 
  21.     } 

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

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

newTask执行完毕。

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

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

来获得函数的返回值。

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

[c-sharp] view plaincopy

  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. private void TryConnect() 
  9.         { 
  10. while (this._running) 
  11.             { 
  12. try
  13.                 { 
  14.                     Ping _ping = new Ping(); 
  15. if (_ping.Send("www.baidu.com").Status == IPStatus.Success) 
  16. this._available.IsNetworkAvailable = true; 
  17. else
  18. this._available.IsNetworkAvailable = false; 
  19.                 } 
  20. catch
  21.                 { 
  22. this._available.IsNetworkAvailable = false; 
  23.                 } 
  24.                 Thread.Sleep(500); 
  25.             } 
  26.         } 
  27. public IsNetworkAvailableDelegate(IsNetworkAvailableWrapper available) 
  28.         { 
  29. this._available = available; 
  30. this._dele = new MyDelegate(this.TryConnect); 
  31. this._result = this._dele.BeginInvoke(null, null); 
  32.         } 
  33. public void EndInvoke() 
  34.         { 
  35. this._running = false; 
  36. this._dele.EndInvoke(this._result); 
  37.         } 
  38.     } 

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

[c-sharp] view plaincopy

  1. private delegate int MyDelegate(int a); 
  2. private int method(int a) 
  3.     Thread.Sleep(10000); 
  4.     Console.WriteLine(a); 
  5. return 100; 
  6. private void MethodCompleted(IAsyncResult asyncResult) 
  7. if (asyncResult == null) return; 
  8.     textBox1.Text = (asyncResult.AsyncState as MyDelegate).EndInvoke(asyncResult).ToString(); 
  9. private void button1_Click(object sender, EventArgs e) 
  10.     MyDelegate my = method; 
  11.     IAsyncResult asyncResult = my.BeginInvoke(MethodCompleted, my); 

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

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

[c-sharp] view plaincopy

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

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

[c-sharp] view plaincopy

  1. Action<object> action=(obj)=>method(obj); 
  2. action.BeginInvoke(obj,ar=>action.EndInvoke(ar),null); 

其实还可以更直接:

[c-sharp] view plaincopy

  1. new Action<object>((obj) => method(obj)).BeginInvoke(ar=>action.EndInvoke(ar), null); 

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

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

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

[c-sharp] view plaincopy

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

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

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

[c-sharp] view plaincopy

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

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。