最近在做系统架构的时候,一个命令需要同时在多个分布节点上执行命令,但主处理又必须等所有节点执行回来后再继续处理,因此研究了一下多线程,现分享如下:
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
大家可以看看.