最近在做系统架构的时候,一个命令需要同时在多个分布节点上执行命令,但主处理又必须等所有节点执行回来后再继续处理,因此研究了一下多线程,现分享如下:
1)第1种方法,微软提供的标准教程:
利用 ManualResetEvent和WaitHandle.WaitAll:
public class Fibonacci { public Fibonacci(int n, ManualResetEvent doneEvent) { _n = n; _doneEvent = doneEvent; } // Wrapper method for use with thread pool. public void ThreadPoolCallback(Object threadContext) { int theThreadNo = (int)threadContext; _doneEvent.Set(); } // Recursive method that calculates the Nth Fibonacci number. public int Calculate(int n) { if (n <= 1) { return n; } return Calculate(n - 1) + Calculate(n - 2); } public int N { get { return _n; } } private int _n; public int FibOfN { get { return _fibOfN; } } private int _fibOfN; private ManualResetEvent _doneEvent; }
下面是调用:
const int FibonacciCalculations = 10; // One event is used for each Fibonacci object ManualResetEvent[] doneEvents = new ManualResetEvent[FibonacciCalculations]; Fibonacci[] fibArray = new Fibonacci[FibonacciCalculations]; Random r = new Random(); // Configure and launch threads using ThreadPool: for (int i = 0; i < FibonacciCalculations; i++) { doneEvents[i] = new ManualResetEvent(false); Fibonacci f = new Fibonacci(r.Next(20, 40), doneEvents[i]); fibArray[i] = f; ThreadPool.QueueUserWorkItem(f.ThreadPoolCallback, i); } // Wait for all threads in pool to calculation... WaitHandle.WaitAll(doneEvents);
但特别注意,在Winform程序中,默认是不支持WaitHandle.WaitAll(doneEvents)的,会报错,需要将程序主函数的STAThread属性除掉。
2)不用线程池,利用线程的Join函数:
public class MyThread { ThreadParam _Param; public Thread CurrThread { get; set; } public ThreadParam Param { get { return _Param; } set { _Param = value; } } Form1 _form; public MyThread(ThreadParam Param,Form1 form) { _Param = Param; _form = form; } // Wrapper method for use with thread pool. private void ThreadPoolCallback() { Form1 threadIndex = _form; for (int i = 0; i < 10; i++) { try { //ConnectionMgmr.CreateConnByDB("AKD001"); //threadIndex.SetValue(_Param.id + ":" + i.ToString()); } catch { //threadIndex.SetValue(_Param.id + ":" + i.ToString() + "=>Error!"); } } _Param.finished = true; } public void Start() { CurrThread = new Thread(ThreadPoolCallback); CurrThread.Start(); } }
调用:
MyThread[] fibArray = new MyThread[1]; for (int i = 0; i < 1; i++) { MyThread f = new MyThread(new ThreadParam() { finished = false, id = i }, this); fibArray[i] = f; f.Start(); } foreach (var item in fibArray) { item.CurrThread.Join(); } this.richTextBox1.AppendText("All is ok!")
public void SetValue(string Text) { if (this.richTextBox1.InvokeRequired == true) { this.richTextBox1.Invoke(new Action<string>(SetValue),Text); } else { this.richTextBox1.AppendText(Text + "\r\n"); } }
(SetValue是Form类里的方法,用于子线程安全调用UI元素)
需要注意的是不能每个线程运行的时候就调用Join函数,如果调用就实际上不是多线程了,就变成了一个个执行了.
除了上面的方法,其实还可以自己利用信号变量来完成。需要特别注意的是如果需要阻塞的主线程是UI线程,子线程不要调用主线程的函数,会造成死锁,我刚开始的时候就因为这个问题,搞得我以为自己的线程方法都有问题。其实很简单,主线程在阻塞,你子线程这个时候去调用它肯定死锁。当然,如果确实需要在子线程中报告数据给UI线程,上面两个方法都不行,需要自己用信号变量轮询,在轮询时去处理UI更新,因为轮询一般都是死循环,需要调用Application.DoEvents(),让UI线程去处理UI事件,下面是轮询结构:
while(true)
{
bool theAllFinished = false;
foreach(var thread in threads)
{
theAllFinished = theAllFinished & thread.Finished;//这个变量Thread没有,自己可以继承来扩展.
}
if(theAllFinished )
{
break;
}
//调用函数让UI线程处理其它事件.
Application.DoEvents();
}
PS:CSDN的写博客的控件真是弱,排版很不友好.
UI线程同步异步处理方面,这篇文章写得不错:
http://www.cnblogs.com/smartls/archive/2011/04/08/2008981.html
大家可以看看.