[轉]http://kb.cnblogs.com/a/888688/
本片文章的議題是有關於傳遞參數到線程的幾種方法。
首先我們要知道什麼是線程,什麼時候要用到線程,如何去使用線程,如何更好的利用線程來完成工作。
線程是程序可執行片段的最小單元,是組成運行時程序的基本單元,一個進程有至少一個線程組成。一般在並行處理等待事件的時候要用到線程,如等待網絡響應,等待I/O通訊,後台事務處理等情況。使用線程其實很簡單,在.net框架下面你首先要定義一個函數來完成一些工作,然後實例化一個線程對象Thread thrd = new Thread(new ThreadStart(線程函數)); 其中ThreadStart是一個不帶參數的函數委托。最後使用thrd.Start()就可以啟動線程了。當然這只是一個很簡單的例子,實際中使用線程會有很多的問題要解決,比如傳遞參數到線程中,等待線程返回,如何同步線程進行同一資源訪問,如何防止死鎖和競爭條件,如何有效的利用線程池,如何提供線程效率等問題。本文在這裡將只對傳遞參數到線程進行探討。
在上面舉例中我們看到線程實例化的參數是一個不帶任何參數的函數委托,那麼就證明了我們不可能通過這樣一個委托傳遞參數到線程內部。那麼我們該怎麼做呢?就我目前所知有三種方法可以實現:1. 利用線程實現類,將調用參數定義成屬性的方式來操作線程參數;2. 利用ParameterizedThreadStart委托來傳遞輸入參數;3. 利用線程池來實現參數傳入。
1. 創建線程實現類,這種方式有個最大的優點就是可以通過線程返回多個返回值
假設存在在一個打印功能的線程,通過主函數傳入打印的行,列和字符數據,並返回打印的字符總數。
class ThreadOutput
{
int _rowCount = 0;
int _colCount = 0;
char _char = '*';
int _ret = 0;
/**//// <summary>
/// 輸入參數
/// </summary>
public int RowCount
{
set { _rowCount = value; }
}
public int ColCount
{
set { _colCount = value; }
}
public char Character
{
set { _char = value; }
}
/**//// <summary>
/// 輸出參數
/// </summary>
public int RetVal
{
get { return _ret; }
}
public void Output()
{
for (int row = 0; row < _rowCount; row++)
{
for (int col = 0; col < _colCount; col++)
{
Console.Write("{0} ", _char);
_ret++;
}
Console.Write("\n");
}
}
ThreadOutput to1 = new ThreadOutput();
to1.RowCount = 10;
to1.ColCount = 20;
Thread thrd = new Thread(new ThreadStart(to1.Output));
// 設置為後台線程,主要是為不影響主線程的結束
thrd.IsBackground = true;
thrd.Start();最後要注意的是由於線程實現類是通過屬性來傳遞數值的,那麼在屬性訪問器中要進行線程同步,否則取得的值可能不正確。
2. 利用ParameterizedThreadStart委托來傳遞輸入參數
ParameterizedThreadStart委托是.Net2.0中才有的。該委托提供來在啟動線程時傳遞一個object參數到線程中。這種方式使用起來比較簡單,但是由於需要對object對象進行類型轉換,所以存在類型不一致的隱患。
3. 利用線程池來實現參數傳入
什麼是線程池,線程池是系統提供一個存放線程的容器,進入線程池後的線程控制權由系統掌握。利用線程池的好處是我們無需為線程存在大量空閒時間而去思考干點別的什麼,適合於常規性的事務處理。在.Net中線程池是一個static類,所以我們需要通過ThreadPool來調用相關的函數。其中向線程池中加入線程的函數就是ThreadPool.QueueUserWorkItem(new WaitCallBack(), object obj)。這個函數有個WaitCallBack的委托,[ComVisibleAttribute(true)]
public delegate void WaitCallback(Object state)
通過這個委托我們也可以傳遞參數到線程中。
其實還有一種方法可以傳遞參數到線程中,那就是通過回調函數來實現,只不過這種方法本質上和第一種通過類的數據成員來傳遞參數的機制一樣。所以就不再細說來!
具體的實現可以參考下面的源代碼:
全部源代碼如下:(vs 2005編譯通過)
1using System;
2using System.Collections.Generic;
3using System.Text;
4using System.Threading;
5
6namespace UsingThread
7{
8 struct RowCol
9 {
10 public int row;
11 public int col;
12 };
13
14 class ThreadOutput
15 {
16 // 建立等待時間
17 static public ManualResetEvent prompt = new ManualResetEvent(false);
18
19 int _rowCount = 0;
20 int _colCount = 0;
21 char _char = '*';
22 int _ret = 0;
23
24 /**//// <summary>
25 /// 輸入參數
26 /// </summary>
27 public int RowCount
28 {
29 set { _rowCount = value; }
30 }
31
32 public int ColCount
33 {
34 set { _colCount = value; }
35 }
36
37 public char Character
38 {
39 set { _char = value; }
40 }
41
42 /**//// <summary>
43 /// 輸出參數
44 /// </summary>
45 public int RetVal
46 {
47 get { return _ret; }
48 }
49
50 public void Output()
51 {
52 for (int row = 0; row < _rowCount; row++)
53 {
54 for (int col = 0; col < _colCount; col++)
55 {
56 Console.Write("{0} ", _char);
57 _ret++;
58 }
59 Console.Write("\n");
60 }
61 // 激活事件
62 prompt.Set();
63 }
64
65 public void Output(Object rc)
66 {
67 RowCol rowCol = (RowCol)rc;
68 for (int i = 0; i < rowCol.row; i++)
69 {
70 for (int j = 0; j < rowCol.col; j++)
71 Console.Write("{0} ", _char);
72 Console.Write("\n");
73 }
74 }
75 }
76
77 class Program
78 {
79 static void Main(string[] args)
80 {
81 ThreadOutput to1 = new ThreadOutput();
82 to1.RowCount = 10;
83 to1.ColCount = 20;
84
85 Thread thrd = new Thread(new ThreadStart(to1.Output));
86 // 設置為後台線程,主要是為不影響主線程的結束
87 thrd.IsBackground = true;
88 thrd.Start();
89
90 // 建立事件等待
91 ThreadOutput.prompt.WaitOne();
92
93 Console.WriteLine("{0}", to1.RetVal);
94
95 ThreadOutput to2 = new ThreadOutput();
96 to2.RowCount = 5;
97 to2.ColCount = 18;
98 to2.Character = '^';
99 Thread thrds = new Thread(new ThreadStart(to2.Output));
100 thrds.IsBackground = true;
101 thrds.Start();
102 thrds.Join();
103
104 Console.WriteLine("{0}", to2.RetVal);
105
106 // 傳遞參數給線程的另一種實現
107 RowCol rc = new RowCol();
108 rc.row = 12;
109 rc.col = 13;
110 to1.Character = '@';
111 if (ThreadPool.QueueUserWorkItem(new WaitCallback(to1.Output), rc))
112 Console.WriteLine("Insert Pool is success!");
113 else
114 Console.WriteLine("Insert Pool is failed!");
115 Thread.Sleep(1000);
116
117
118 // 使用新的ThreadStart委托來傳遞參數
119 rc.col = 19;
120 to2.Character = '#';
121 Thread thrdt = new Thread(new ParameterizedThreadStart(to2.Output));
122 thrdt.Start(rc);
123 thrdt.Join();
124 }
125
126 }
127}
128
a1 = 0x01; //0000 0001
a2 = 0x00; //0000 0000
a3 = 0x03; //0000 0011
a4 = 0x02; //0000 0010
b1 = a1 ^ a2; //0000 0001
b2 = a1 ^ a3; //0000 0010
b3 = a1 ^ a4; //0000 0011
^異或運算符,位值相同為0,不同為1,見上示例.
//
簡單實際問題舉例:
======\=======\=======
======a=======b=======
上面是2條電路,2個開關分別為a和b,打開狀態:\[1],關閉狀態:/[0].
若同時打開或者關閉,兩條電路均不通.
若a打開[1],b關閉[0],電路1通電
======\=======/=======
若a關閉[0],b打開[1],電路2通電
======/=======\=======
綜上,電路在a,b狀態相同時不通[0],在a,b不同時通電[1].
a1 = 0x01; //0000 0001
a2 = 0x00; //0000 0000
a3 = 0x03; //0000 0011
a4 = 0x02; //0000 0010
b1 = a1 ^ a2; //0000 0001
b2 = a1 ^ a3; //0000 0010
b3 = a1 ^ a4; //0000 0011
^異或運算符,位值相同為0,不同為1,見上示例.
//
簡單實際問題舉例:
======\=======\=======
======a=======b=======
上面是2條電路,2個開關分別為a和b,打開狀態:\[1],關閉狀態:/[0].
若同時打開或者關閉,兩條電路均不通.
若a打開[1],b關閉[0],電路1通電
======\=======/=======
若a關閉[0],b打開[1],電路2通電
======/=======\=======
綜上,電路在a,b狀態相同時不通[0],在a,b不同時通電[1].