資料結構與 c++ 程式設計 進階
DESCRIPTION
資料結構與 C++ 程式設計 進階. 多執行緒( multi-thread )程式設計. 大綱. 前言 執行緒( thread ) 執行緒與平行程式設計( Parallel Programming ) 第一支多執行緒程式 使用 Event 旗標 同步化問題( Synchronization ) 使用 Critical Section. 前言. 程式 (Program) 儲存於硬碟中的可執行檔稱為 Program 行程 (Process) 載入記憶體中的可執行檔稱為 Process 執行緒 (Thread) - PowerPoint PPT PresentationTRANSCRIPT
資料結構與 C++ 程式設計進階
多執行緒( multi-thread )程式設計
NTU TRAININGC/C++ Page 2
大綱 前言 執行緒( thread ) 執行緒與平行程式設計( Parallel
Programming ) 第一支多執行緒程式 使用 Event 旗標 同步化問題( Synchronization ) 使用 Critical Section
NTU TRAININGC/C++ Page 3
前言 程式 (Program)
儲存於硬碟中的可執行檔稱為 Program
行程 (Process) 載入記憶體中的可執行檔稱為 Process
執行緒 (Thread) Process 中一段程式碼的執行序列稱為 Thread ,是作
業系統能夠進行運算與排程的最小單位。
NTU TRAININGC/C++ Page 4
執行緒( thread ) 簡介
執行緒( thread )是被包含在行程( Process )中實際運作的單位。
一個行程中可以並行多個執行緒,而這些執行緒共用同一份行程的系統資源、名稱位址等等。
signal, fd, global var.. etc
Thread 1
Thread 2
Thread 3
Process 1
signal, fd, global var.. etc
Thread 1
Thread 2
Thread 3
Process 2
NTU TRAININGC/C++ Page 5
執行緒與平行處理 平行程式設計( Parallel Programming )
將一項任務切成較小、並且可同時處理的子任務,在分配到多核心的處理器上以達到更高的效能。
單一執行緒與多執行緒程式比較
CPU0 CPU1
Process1
Time
CPU0 CPU1
Thread1
Time
Thread2
NTU TRAININGC/C++
使用 _beginthread() 產生執行緒 _beginthread ( 執行緒要執行的函式 , 堆疊大小 , 變數位址 ) ;
Page 6
第一支執行緒程式
#include <process.h> // thread API#include <stdio.h> #include <windows.h> // Sleep() APIvoid Thread( void* pParams ) { while(1) { printf("Thread and Data: %d\n", *(int *)pParams); Sleep(1000); }}int main () { int n = 5; _beginthread( Thread, 0, &n); while( 1 ) { printf("This is main() process.\n"); getchar(); } return 0;}
NTU TRAININGC/C++ Page 7
小練習 01 呈上頁範例,試著使用 getch() 擷取鍵盤的 +, -
鍵,產生一個執行緒的使之可以產生一個時鐘的計數器。 參考範例:
http://w.csie.org/~r97944012/train/p10-thread-timer.exe
NTU TRAININGC/C++ Page 8
一次產生多個執行緒
#include <process.h>#include <stdlib.h>#include <stdio.h> #define N 20
typedef struct _complex_data{ int iVal; double fVal;} cData;
// 重點一、多執行緒// 重點二、執行緒的資料傳遞方法(傳指標) void Thread( void* pParams ) { cData *pData = (cData *)pParams; printf("%d %.2lf\n", pData->iVal, pData->fVal);}
使用 _beginthread() 產生執行緒 _beginthread ( 執行緒要執行的函式 , 堆疊大小 , 變數位址 ) ;
int main () { int i; cData tData[N]; for(i = 0; i < N; i++) { tData[i].iVal = i; tData[i].fVal = i + 0.5; _beginthread( Thread, 0, &tData[i]); } printf("main()\n"); system("pause"); return 0;}
NTU TRAININGC/C++ Page 9
使用 Event 旗標 呈上頁範例,我們發現 main() 的主行程並不會等
待所有的執行緒結束。 使用 Event 旗標等待 Thread 結束
(1) 宣告旗標變數 HANDLE 旗標變數 ;
(2) 初始化旗標 CreateEvent( 屬性 , 手動旗標 , 初始值 , 名稱 )
(3) 設定旗標 SetEvent( 旗標 );
(4) 等待信號 WaitForSingleObject( 旗標變數 , 等待時間 (ms 或 INFINITE)); WaitForMultipleObjects( 陣列大小 , 旗標變數陣列 , 等待所有旗標 , 等
待時間 )
Thread 2
Thread 1
Main process
NTU TRAININGC/C++ Page 10
等待多個執行緒才終止主行程
#include <process.h>#include <stdlib.h>#include <windows.h>#include <stdio.h> #define N 20
HANDLE hEvent[N];typedef struct _complex_data{ int tID; int iVal; double fVal;} cData;
void Thread( void* pParams ) { cData *pData = (cData *)pParams; printf("%d %.2lf\n", pData->iVal, pData->fVal); SetEvent(hEvent[pData->tID]);}
使用 SetEvent() 搭配 WaitForMultipleObjects()int main () { int i; cData tData[N];
for(i = 0; i < N; i++) { hEvent[i] = CreateEvent( NULL, FALSE, FALSE, NULL ); tData[i].tID = i; tData[i].iVal = i; tData[i].fVal = i + 0.5; _beginthread( Thread, 0, &tData[i]); } WaitForMultipleObjects(N , hEvent, TRUE, INFINITE); printf("main()\n"); system("pause"); return 0;}
NTU TRAININGC/C++ Page 11
小練習 02 矩陣運算
p11-matrix.cpp 是個可計算 500 x 500 的矩陣運算程式,試著建立 2~N 個 thread 將它進行平行計算,並試著估計一下優化的結果。
( 提示:若開啟 4 個 thread ,則將 C 矩陣拆成 1/4 運算。 )
A B C
NTU TRAININGC/C++ Page 12
執行緒的同步化問題範例#include <process.h>#include <stdio.h>#define N 10000000int x; void ThreadJob( void* pParams ) { int i; for ( i = 0; i < N; i++ ) x = x+1; } int main () { _beginthread( ThreadJob, 0, NULL ); _beginthread( ThreadJob, 0, NULL ); while( 1 ) { printf("x = %d\n", x); getchar(); } return 0;}
NTU TRAININGC/C++ Page 13
同步化問題( Synchronization ) 上一頁的範例,會產生一個 race condition 的問題。 race condition 定義
兩個以上的行程(或執行緒)共用同一資源時,因為共用同一個系統資源,在進行存取時會「因為執行的順序不同,導致結果不一致。」
通常,在多個行程同時進行「寫入」的動作時,才會發生 race condition 。舉例來說,有兩個行程同時想要將記憶體中的 X 變數加上一,有可能發生下列兩種情形:
Read X = 1Add X with 1--Write X = 2-
--Read X = 1Add X with 1-Write X = 2
Process 1 Process 2
Read X = 1Add X with 1Write X = 2---
---Read X = 2Add X with 1Write X = 3
Process 1 Process 2
NTU TRAININGC/C++ Page 14
同步化問題( Synchronization )之解決 要解決 race condition 導致執行緒之間的資料不同步的
問題。通常我們會將執行緒對於記憶體的存取改為atomic operation :也就是「將讀取和寫入」某共享資源的動作,一次性的完成,並且期間不允許其他的行程打斷。
實作方式: (1) 宣告旗標變數
CRITICAL_SECTION cs;
(2) 初始化 InitializeCriticalSection( &cs );
(3) 將要設定為 atomic operation 的區域設為 critical section EnterCriticalSection( &cs ); // …… atomic operation LeaveCriticalSection( &cs );
NTU TRAININGC/C++ Page 15
小練習 03 試著將投影片第 12 頁的範例加上 critical
section ,解決 race condition 的問題。