當面對一個耗時較長的任務時,我們可以把這個任務切分成多個部分,然後同時交給多個線程處理。
□ 統計字節數組一個比較耗時的方式
以下來統計一個字節數組的大小。
class Program{static byte[] values = new byte[500000000];static void Main(string[] args){GenerateByteArray();Console.WriteLine("正在統計字節數");Stopwatch watch = new Stopwatch();watch.Start();long total = 0;for (int i = 0; i < values.Length; i++){total += values[i];}watch.Stop();Console.WriteLine("統計結果為:" + total);Console.WriteLine("計算時間為:" + watch.Elapsed);}static void GenerateByteArray(){var r = new Random(987);for (int i = 0; i < values.Length; i++){values[i] = (byte)r.Next(10);}}}
如果把統計工作同時交給多個線程,是否可以把統計時間省下來呢?
□ 同時使用多個線程
現在要對"統計字節數組大小"這個任務進行均分、切分。首先面臨的問題是:按什麼標准均分?
--這個完全是靠個人喜好,可以讓2個線程,3個線程......來處理。在這裡,就根據CPU的數量來均分,因為CPU的數量可以通過Environment.ProcessorCount獲得。
面臨的第二問題是:均分什麼?
--比如有4個CPU
--那可以把任務分成4個線程同時處理
--把字節數組的長度均分,比如字節數組的長度是1000,均分成4段,每段長度為250
--把字節數組的大小分成4個放一個數組裡,即[sum1, sum2, sum3, sum4],所有的元素加起來就是字節數組的總大小
class Program{static byte[] values = new byte[500000000];//分段統計的大小放該數組,比如分成4等份,[10000,10005,10008,10009]private static long[] partialSum;//把values數組長度均等分,比如長度1000,分成4粉,那partialSize就是250private static int partialSize;static void Main(string[] args){//根據CPU的數量確定數組的長度partialSum = new long[Environment.ProcessorCount];//根據CPU的數量確定數組長度均等分partialSize = values.Length/Environment.ProcessorCount;GenerateByteArray();Console.WriteLine("正在統計字節數");Stopwatch watch = new Stopwatch();watch.Start();long total = 0;for (int i = 0; i < values.Length; i++){total += values[i];}watch.Stop();Console.WriteLine("統計結果為:" + total);Console.WriteLine("計算時間為:" + watch.Elapsed);Console.WriteLine();watch.Reset();watch.Start();Thread[] threads = new Thread[Environment.ProcessorCount];for (int i = 0; i < Environment.ProcessorCount; i++){threads[i] = new Thread(SumPartial);threads[i].Start(i);}//保證一個線程結束再執行下一個線程for (int i = 0; i < Environment.ProcessorCount; i++){threads[i].Join();}//統計總大小long total2 = 0;for (int i = 0; i < Environment.ProcessorCount; i++){total2 += partialSum[i];}watch.Stop();Console.WriteLine("使用分段線程統計的大小:" + total2);Console.WriteLine("計算時間為:" + watch.Elapsed);}/// <summary>/// 分段統計字節數組的大小/// </summary>/// <param name="partialNumber">比如有4個CPU,partialNumber可能的值是0, 1, 2, 3</param>static void SumPartial(object partialNumber){long sum = 0;int partialNumberAsInt = (int)partialNumber;int baseIndex = partialNumberAsInt * partialSize;for (int i = baseIndex; i < baseIndex + partialSize; i++){sum += values[i];}partialSum[partialNumberAsInt] = sum;}/// <summary>/// 創建字節數組/// </summary>static void GenerateByteArray(){var r = new Random(987);for (int i = 0; i < values.Length; i++){values[i] = (byte)r.Next(10);}}}
以上,統計字節數組大小的方式倒不是最重要的,線程部分才是重點:
○ 有幾個CPU,就有幾個線程
○ 線程的實例方法Start可以傳遞object類型的參數
○ 線程的實例方法Join,用來保證執行完上一個線程再執行下一個線程
在這裡,使用多線程同時處理一個任務,效率差不多提高了2.6倍!
總結:
○ 對於一個比較耗時的任務可以同時交給多個線程處理
○ 線程的實例方法Join保證執行完上一個線程再執行下一個線程
線程系列包括:
你提到的線程函數是一般函數還是生成線程的函數,或是用於運行特定函數的線程?
如果是在用VC中想用多個線程運行同樣的任務,而這些任務之間沒有通訊問題和內存共享的問題,那可以用下面的模板:
DWORD WINAPI TaskThreadProc(LPVOID)
{
// 獨立任務
// ...
}
void RunTaskUseMultiThread(int nThreadCount) // 線程個數為nThreadCount
{
HANDLE* threads = new HANDLE[nThreadCount]; // 線程句柄,對線程進行狀態的控制時用到
DWORD* idThreads = new DWORD[nThreadCount]; // 線程id,給線程發消息時用到
int i;
for (i = 0; i < nThreadCount; i++)
{
threads[i] = CreateThread( NULL, 0, TaskThreadProc, NULL, CREATE_SUSPENDED, &idThreads[i]); // 創建線程
}
for (i = 0; i < nThreadCount; i++) // 由於生成的是CREATE_SUSPENDED的線程,所以這裡開始讓線程開始運行
ResumeThread(threads[i]);
WaitForMultipleObjects(nThreadCount, threads, TRUE, INFINITE); // 等待線程運行結束
for (i = 0; i < nThreadCount; i++)
CloseHandle(threads[j]); // 銷毀線程
delete [] threads;
delete [] idThreads;
}
首先明確一點,單核CPU,多線程運算,其實是每個線程申請一段時間,只不過時間太短,好像是同時執行,耗時運算,一般會加入等待界面並新啟一個線程運算,運算完回調,效率問題一個硬件解決,一個優化算法