Thread Basics

41
Thread Basics Thread Basics 請請請請請請請請 請請請請請

description

請以純粹黑白列印. Thread Basics. 井民全製作. 包含 function parameters 與 Local variables. OS 用來管理 thread 使用. Introduction. 用來存放程式碼. Process consists Process kernel object + address space Thread consists Thread kernel object + thread stack Thread 執行 process ’ s address space 中的 程式碼 - PowerPoint PPT Presentation

Transcript of Thread Basics

Page 1: Thread Basics

Thread BasicsThread Basics

請以純粹黑白列印

井民全製作

Page 2: Thread Basics

IntroductionIntroduction

Process consistsProcess consists Process kernel object + address spaceProcess kernel object + address space

Thread consistsThread consists Thread Thread kernel objectkernel object + + thread stackthread stack

Thread Thread 執行 執行 process’s address space process’s address space 中的中的程式碼程式碼 處理放在該 處理放在該 address space address space 中的中的變數變數

包含 function parameters 與

Local variables

包含 function parameters 與

Local variables OS 用來管理 thread 使用OS 用來管理 thread 使用

用來存放程式碼用來存放程式碼

Thread 擁有自己的 STACK

Page 3: Thread Basics

IntroductionIntroduction

Process Process 本身是靜止不動的本身是靜止不動的 一個行程一個行程從不執行任何程式碼從不執行任何程式碼,它只是一,它只是一

個 個 thread thread 的的容器容器 Thread Thread 總是在某個行程的「執行內容總是在某個行程的「執行內容(( contextcontext )」中被建立,並且在該行程)」中被建立,並且在該行程中度過它們的一生 中度過它們的一生

當 thread function 執行結束則 thread 生命也就結束

當 thread function 執行結束則 thread 生命也就結束

Page 4: Thread Basics

IntroductionIntroduction

If you have If you have two threadstwo threads running in the running in the context of a single processcontext of a single process they they shareshare the same address space the same address space

Threads can Threads can share kernel object share kernel object handleshandles

Process Process 因為需要獨立的 因為需要獨立的 address space,address space,故比起 故比起 thread thread 來說來說 ,, 消耗更多資源消耗更多資源

因為 kernel object Table 以 process 為單位放置

因為 kernel object Table 以 process 為單位放置

Thread 只需一個Kernel object 與 stack

Thread 只需一個Kernel object 與 stack Virtual address space 需要許多的紀錄 ,

.exe 與 .dll 也需要 file resource

Virtual address space 需要許多的紀錄 ,.exe 與 .dll 也需要 file resource

Page 5: Thread Basics

A path of execution with a A path of execution with a processprocess

Create a primary thread

Create a primary thread

C/C++ run-timeStartup code

C/C++ run-timeStartup code

Entry-point functionEntry-point function main, wmain,WinMain, wWinMain

main, wmain,WinMain, wWinMain

void main(){

你的程式碼}

呼叫 ExitProcess結束

呼叫 ExitProcess結束

Process起始

Process結束

注意 : 這是 primaryThread 執行的流程

Page 6: Thread Basics

Writing your First Thread Writing your First Thread FunctionFunction

DWORD WINAPI ThreadFunc(PVOID pvParam){ DWORD dwResult = 0; …

return (dwResult);}

DWORD WINAPI ThreadFunc(PVOID pvParam){ DWORD dwResult = 0; …

return (dwResult);}

可以是任意名稱可以是任意名稱

盡量使用 local 變數以免除同步的問題

盡量使用 local 變數以免除同步的問題

呼叫者可以傳進任意變數型態

呼叫者可以傳進任意變數型態

當 thread function return時 , thread 自動消滅

Page 7: Thread Basics

The Create Thread FunctionThe Create Thread Function 如何建立如何建立 ThreadThread

HANDLE CreateThread( PSECURITY_ATTRIBUTES psa;

DWORD cbStack; PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam,

DWORD fdwCreate, PDWORD pdwThreadID);

HANDLE CreateThread( PSECURITY_ATTRIBUTES psa;

DWORD cbStack; PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam,

DWORD fdwCreate, PDWORD pdwThreadID);

*NULL=> 預設 security* 若你希望 ChildProcess 可以 存取這個新的 thread, 則

*NULL=> 預設 security* 若你希望 ChildProcess 可以 存取這個新的 thread, 則

給定使用的堆疊大小 , 0 預設值 給定使用的堆疊大小 , 0 預設值

給定使用的參數給定使用的參數

SECURITY_ATTRIBUTES sa;sa.nLength=sizeof(sa);sa.lpSecurityDescriptor=NULL;sa.bInheritHandle=TRUE;

建立新的 sa 丟進去建立新的 sa 丟進去指定 Thread 執行的程式也就是前面的 thread fun

指定 Thread 執行的程式也就是前面的 thread fun

建立的方式建立的方式 0 立即執行CREATE_SUSPENDED 等到呼叫 ResumeThread 後才執行

回傳一個 ThreadID回傳一個 ThreadID

Page 8: Thread Basics

馬上寫個範例馬上寫個範例

CreateThread(…)DWORD WINAPI FirstThread(PVOID pvParam){

}

DWORD WINAPI FirstThread(PVOID pvParam){

}

Primary threadPrimary thread

建立的 thread建立的 thread

HANDLE hThread=CreateThread(NULL,0, FirstThread,(PVOID)Data,0,&dwThreadID);

預設 security預設 security

預設 Stack Size預設 Stack Size

設定 thread fun設定 thread fun

繼續執行繼續執行

傳給 thread fun的參數

傳給 thread fun的參數

建立好 thread 後立即執行

建立好 thread 後立即執行

回傳的 thread ID回傳的 thread ID

Thread functionThread function

執行的過程執行的過程

Page 9: Thread Basics

馬上寫個範例馬上寫個範例

int APIENTRY WinWain( … ){int Data[2]={10,0};DWORD dwThreadID;

HANDLE hThread=CreateThread(NULL,0,FirstThread,(PVOID)Data,0,&dwThreadID);

WaitForSingleObject(hThread,INFINITE);

TCHAR Message[100];wsprintf(Message,_T(" 答案為 %d"),Data[1]);MessageBox(NULL,Message,_T("1+2+3+...+10 答案 "),MB_OK);

CloseHandle(hThread);return 0;

}

DWORD WINAPI FirstThread(PVOID pvParam){

int* Data=(int*) pvParam;

int sum=0;for(int i=0;i<=*Data;i++)

sum+=i;Data[1]=sum;

return(0);}

DWORD WINAPI FirstThread(PVOID pvParam){

int* Data=(int*) pvParam;

int sum=0;for(int i=0;i<=*Data;i++)

sum+=i;Data[1]=sum;

return(0);}

等待 thread 結束等待 thread 結束

立即執行立即執行

組合你的答案組合你的答案

Win32CreateThreadWin32CreateThread

Local 變數配置在 thread自己的 stack 中

Local 變數配置在 thread自己的 stack 中

Page 10: Thread Basics

系統建立 系統建立 thread thread 的流程的流程呼叫 Create Thread呼叫 Create Thread

系統建立* Thread kernel object* Usage counter = 2

系統建立* Thread kernel object* Usage counter = 2

Step 1 Thread 本身 reference與 primary thread reference

Thread 本身 reference與 primary thread reference

設定 kernel 初值 1. Suspend count =1 2. Exit code =STILL_ACTIVE 3. signaled = FALSE

設定 kernel 初值 1. Suspend count =1 2. Exit code =STILL_ACTIVE 3. signaled = FALSE

由 process 的 virtural address 中配置 Stack 空間給 thread 使用

由 process 的 virtural address 中配置 Stack 空間給 thread 使用

Step 2

Step 3

把 thread fun 位址 與 參數 push 到 thread 專屬 Stack 中

把 thread fun 位址 與 參數 push 到 thread 專屬 Stack 中

Step 4

設定 kernel Object 中 contextSP 與 IP + 一堆 CPU 暫存器

設定 kernel Object 中 contextSP 與 IP + 一堆 CPU 暫存器

Step 5

儲存 CPU 的狀態儲存 CPU 的狀態

目前執行的程式碼位置目前執行的程式碼位置

VOID BaseThreadStar(){}

VOID BaseThreadStar(){}

執行你的 thread function執行你的 thread function

ExitThread()ExitThread()

先暫停先暫停

呼叫呼叫

Page 11: Thread Basics

Thread Kernel Thread Kernel 結構圖 結構圖 Thread Kernel Object

的內容Thread Kernel Object

的內容

SPIP

其他 CPU registers

SPIP

其他 CPU registers

ContextContext

Usage count = 2Suspend count =1Exit code = STILL_ACTIVESignated = FALSE

Usage count = 2Suspend count =1Exit code = STILL_ACTIVESignated = FALSE

高位址高位址

低位址低位址

pvParampfnStartAddr

參數參數

Thread function起始位址

Thread function起始位址

VOID BaseThreadStart(){ …}

VOID BaseThreadStart(){ …}

Kernel32.dllKernel32.dll

Thread StackThread Stack

指向 Stack top 的資料指向 Stack top 的資料

指向要執行的 function指向要執行的 function

Page 12: Thread Basics

接下來看看接下來看看 BaseThreadStart BaseThreadStart 函函式式

的工作情形的工作情形VOID BaseThreadStart(PTHREAD_START_ROUTINE pfnStartAddr, PVOID pvParam) { __try {

ExitThread( (pfnStartAddr)(pvParam) ); } __except(UnhandledExceptionFilter(GetExceptionInformation( ))) {

ExitProcess(GetExceptionCode( ));

} // NOTE: We never get here. }

Kernel32.dllKernel32.dll

呼叫你的 thread function呼叫你的 thread function

thread function參數位址

thread function參數位址

當發生 Exception 時 ,會終結 Process

當發生 Exception 時 ,會終結 Process

當執行完 thread fun直接呼叫 ExitThread

當執行完 thread fun直接呼叫 ExitThread

11

2233

44

Page 13: Thread Basics

啟動 啟動 threadthread

是否設定 CREATE_SUSPEND

是否設定 CREATE_SUSPEND

等到 Resume才啟動

等到 Resume才啟動

Yes

No

設定 Suspend count =0

設定 Suspend count =0

現在這個 thread可以被 schedule 到CPU 上執行了

將 context 中的暫存器值載入 CPU

將 context 中的暫存器值載入 CPU

現在這個 thread可以執行程式碼 以及處理資料了

執行 BaseThreadStart function

執行 BaseThreadStart function

ExitThread 結束ExitThread 結束

BaseThreadStart 內部呼叫ExitThread 結束 thread 生命

Page 14: Thread Basics

我們都知道 main 是由 primary thread 所執行 .

那麼 primary thread 是怎麼被建立的呢 ?

VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnStartAddr) { __try { ExitThread((pfnStartAddr)( ));

} __except(UnhandledExceptionFilter(GetExceptionInformation( ))) {

ExitProcess(GetExceptionCode( )); }}

VOID BaseProcessStart(PPROCESS_START_ROUTINE pfnStartAddr) { __try { ExitThread((pfnStartAddr)( ));

} __except(UnhandledExceptionFilter(GetExceptionInformation( ))) {

ExitProcess(GetExceptionCode( )); }}

C/C++ Startup Code

Page 15: Thread Basics

Terminating a ThreadTerminating a Thread終結終結 thread thread 的方法的方法

The thread function returnsThe thread function returns ExitThread()ExitThread()

C++ C++ 物件的物件的解構子解構子 ,, 不會被呼叫不會被呼叫 DLL DLL 會被通知會被通知

TerminateThread(HANDLE, DWORD)TerminateThread(HANDLE, DWORD) 不一定會呼叫完不一定會呼叫完 ,, 指定的 指定的 thread thread 就會被刪除就會被刪除 載入的 載入的 DLL DLL 不會接到通知不會接到通知

包含 包含 thread thread 的 的 process process 被 被 terminateterminate

請使用這個請使用這個

不要使用不要使用不要使用不要使用

不要使用不要使用

Page 16: Thread Basics

When a Process terminateWhen a Process terminate

If you call ExitIf you call ExitProcessProcess and and TerminateTerminateProcess Process terminate all threads terminate all threads in the processin the process ExitThreadExitThread & & TerminateThreadTerminateThread only only

terminate a threadterminate a thread 在 Thread 中的 C++ 自動變數將無法被 destroy

在 Thread 中的 C++ 自動變數將無法被 destroy

ExitProcess 與 ExitThread 的比較

Page 17: Thread Basics

當 當 thread thread 被終結時被終結時 ,, 系統的行為是甚麼系統的行為是甚麼 ??

All All User object handleUser object handle owned by the owned by the thread are thread are freefree

Thread’s exit code: STILL_ACTIVE 傳進 ExitThread( 參數 )

Thread’s exit code: STILL_ACTIVE 傳進 ExitThread( 參數 )

The kernel object signaled

The kernel object signaled

若這是最後一個Thread , 則刪除 process

若這是最後一個Thread , 則刪除 process -1 Thread kernel Object

usage count 減一Thread kernel Object

usage count 減一

清除所有的 Window 與 Hook ( 攔截 )清除所有的 Window 與 Hook ( 攔截 )

Page 18: Thread Basics

我要如何知道 我要如何知道 thread thread 的狀態的狀態呢呢 ??

BOOL GetExitCodeThread(HANDLE hThread, PDWORD pdwExitCode);

BOOL GetExitCodeThread(HANDLE hThread, PDWORD pdwExitCode);

HANDLE hThread=CreateThread(NULL,0,FirstThread,(PVOID)Data,0, &dwThreadID);

WaitForSingleObject(hThread,INFINITE);

DWORD dwExitCode; GetExitCodeThread(hThread,&dwExitCode);

CloseHandle(hThread);得到 Thread exit code得到 Thread exit code

使用範例使用範例

呼叫 GetExitCodeThre

ad function

Page 19: Thread Basics

如果你有用到 如果你有用到 C/C++ function,C/C++ function,請使用 請使用 _beginthreadex _beginthreadex 代替代替

uintptr_t _beginthreadex( void *security, unsigned stack_size, unsigned ( __stdcall *start_address )( void * ), void *arglist, unsigned initflag, unsigned *thrdaddr );

HANDLE CreateThread( LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId );

你發覺了嗎 ?參數數目一樣 , 但是

資料型態不同 !

Microsoft’s C/C++ 程式庫開發小組認為 c程式碼不應該 depend

on Windows

Page 20: Thread Basics

請使用 請使用 _beginthreadex_beginthreadex

直接替換會有問題直接替換會有問題 !!

hThread=CreateThread(NULL,0,FirstThread,(PVOID)Data,0,&dwThreadID);

hThread=_beginthreadex(NULL,0,FirstThread,(PVOID)Data,0,&dwThreadID);

你應該要轉型才對 !

Page 21: Thread Basics

請使用 請使用 _beginthreadex_beginthreadex 簡單的巨集簡單的巨集

#include <process.h> #include <process.h>

換行一定要加 \換行一定要加 \定義巨集定義巨集

typedef unsigned (__stdcall *PTHREAD_START) (void *);#define chBEGINTHREADEX (psa,cbStack,pfnStartAddr, pvParam,fdwCreate, pdwThreadID) \ ((HANDLE) _beginthreadex( \ (void*) (psa), \

(unsigned)(cbStack),\ (PTHREAD_START) (pfnStartAddr),\ (void *) (pvParam),\ (unsigned)(fdwCreate),\ (unsigned*) (pdwThreadID)))

用巨集幫我轉型

hThread=chBEGINTHREADEX(NULL,0,FirstThread,(PVOID)Data,0,&dwThreadID);

_beginthreadex 簡單範例

別忘了加上 process.h別忘了加上 process.h

Page 22: Thread Basics

Global Variable & Local Global Variable & Local VariableVariable

int golbal=1;void Fun( void* p){ int x=123;

}

1 號 thread 2 號 thread

x=123

x=123

Golbal=1

存取自己的 Stack中的 local variable

存取自己的 Stack中的 local variable

存取自己的 Stack中的 local variable

存取自己的 Stack中的 local variable

Page 23: Thread Basics

Global Variable & Local Global Variable & Local VariableVariable驗證程式驗證程式#include <windows.h>

#include <process.h>#include <stdio.h>

int global;void fun(void *p){

char *name=(char*) p;int x=123;for(int i=0;i<5;i++){

printf("===== %s ======\n",name);printf("x 變數的位址 ==> %p \n",&x);printf(" 全區域變數的位址 ==> %p \n\

n",&global);Sleep(1000);

}}

int main(){_beginthread(fun,0," 一號 ");_beginthread(fun,0,"二號 ");

getchar();return 0;

}

驗證 Local Variable

關鍵段落 :請注意兩個 thread

列印出的位址

關鍵段落 :請注意兩個 thread

列印出的位址

Page 24: Thread Basics

What’s Problem?What’s Problem?

因為 因為 VC VC 預設的 預設的 C/C++ run-time library C/C++ run-time library 為 為 single-threaded, single-threaded, 所以你必須選擇 所以你必須選擇 multithreadmultithread 版本的 版本的 runtime libraryruntime library

error C2065: '_beginthreadex' : undeclared identifier

Project->Settings…Project->Settings…VC 6 的設定

Page 25: Thread Basics

Visual Studio .Net Visual Studio .Net 設定設定

Page 26: Thread Basics

Six C/C++ run-time librariesSix C/C++ run-time librariesLibrary NameLibrary Name DescriptionDescription

LibC.libLibC.lib Statically linked library for Statically linked library for single single threadthread application application

LibCD.libLibCD.lib 同上同上 , Debug , Debug 版本版本

LibCMt.libLibCMt.lib Statically linked library for Statically linked library for multithreadmultithread application application

LibCMtD.libLibCMtD.lib 同上同上 , Debug , Debug 版本版本

MSVCRt.libMSVCRt.lib Import library for MSVCRt.dllImport library for MSVCRt.dll

(( 支援 支援 single thread single thread 與 與 mutlithread)mutlithread)

MSVCRtD.libMSVCRtD.lib 同上同上 , Debug , Debug 版本版本

預設值預設值

為何要那麼多 libraries 選項 ?為何不一次搞定 ?

主要是因為 1970年時 , C run-time

library 開發人員沒有考慮到 multi-thread 的情況

Page 27: Thread Basics

Standard C/C++ run-time Standard C/C++ run-time library library 並沒有支援 並沒有支援 multithreadmultithread

請看下面程式碼請看下面程式碼 , , 當 當 system system 呼叫與 呼叫與 if if 之間之間 ,, 有 有 thread thread 切換切換 , , 則 則 global global 變數變數 errno errno 則會發生問題 則會發生問題

BOOL fFailure=(system(“NOTEPAD.exe README.txt”) == -1);if(fFailure){ switch(errno){ case E2BIG: break; … }}

BOOL fFailure=(system(“NOTEPAD.exe README.txt”) == -1);if(fFailure){ switch(errno){ case E2BIG: break; … }}

因為 errno 是全區域變數所以任何 thread 都可能會更改它

因為 errno 是全區域變數所以任何 thread 都可能會更改它

因此導致程式產生不確定的結果

C/C++ run-time library 程式片段C/C++ run-time library 程式片段

Page 28: Thread Basics

Standard C/C++ run-time Standard C/C++ run-time library library 並沒有支援 並沒有支援 multithreadmultithread

C/C++ run-time library

大量使用 global

variable 處理狀態

C/C++ run-time library

大量使用 global

variable 處理狀態

諸如 errno,_doserrno, strtok,_wcstor, strerror, tmpnam, tmpfile, asctime, …

諸如 errno,_doserrno, strtok,_wcstor, strerror, tmpnam, tmpfile, asctime, …

BOOL fFailure=(system(“NOTEPAD.exe README.txt”) == -1);if(fFailure){ switch( errno ){ case E2BIG: break; … }}

BOOL fFailure=(system(“NOTEPAD.exe README.txt”) == -1);if(fFailure){ switch( errno ){ case E2BIG: break; … }}

對於這種全區域變數的存取必須做轉換

對於這種全區域變數的存取必須做轉換

error 轉換成 function call

#define errno (*_errno( ))

Page 29: Thread Basics

程式設計技巧程式設計技巧 經全區域變數的存取經全區域變數的存取轉換轉換成內部成內部 變數存取 變數存取

int Myerrno=123;

int* _errno(){ return &Myerrno;}

void main(){ * _errno()=555;

}

Thread 內部使用的區域

傳回 Myerrno 的位址

errno= 555; 轉換為 thread

內部變數 Myerrno=555;

int x = errno;轉換成 int x= *_errno();

設定 errno 值 讀取 errno 值

Page 30: Thread Basics

C/C++ run-time library C/C++ run-time library 設定設定

在 在 multi-thread multi-thread 環境下環境下 , run-time , run-time library library 的全區域變數存取的全區域變數存取 ,, 需要轉換需要轉換 . . 所所以你必須 以你必須 link multi-thread link multi-thread 專用的 專用的 library.library.

接下來 , 就要來看看 C/C++ run-time

library 的詳細實作情形了

Page 31: Thread Basics

來看看為甚麼 來看看為甚麼 _beginthreadex _beginthreadex 可以可以全區域變數全區域變數的解決問題 的解決問題 ??

C/C++ run-time source code C/C++ run-time source code Microsoft Visual Studio .NET 2003\Vc7\crt\Microsoft Visual Studio .NET 2003\Vc7\crt\

srcsrc threadex.cthreadex.c

有關係的幾個 有關係的幾個 C/C++ C/C++ 處理 處理 thread thread 的的functionfunction _beginthreadex_beginthreadex(): (): 你建立新你建立新 threadthread必須呼叫必須呼叫 _threadstartex(): _threadstartex(): 由 由 _beginthreadex _beginthreadex 所呼叫所呼叫 _endthreadex_endthreadex : : 當結束 當結束 thread thread 時時 ,, 自動呼叫自動呼叫

用來解除暫存空間使用用來解除暫存空間使用

11

22

33

Page 32: Thread Basics

三個三個 functionsfunctions 之間的關係之間的關係

_beginthreadex(指定 callback function)_beginthreadex(指定 callback function)

CreateThread(… _threadstartex_threadstartex,…),…)

_beginstartex(指定 你的 thread function位址 )

_beginstartex(指定 你的 thread function位址 )

呼叫你的 thread function呼叫你的 thread function

呼叫 _endthread() 釋放記憶體

呼叫 _endthread() 釋放記憶體

配置 Thread 專用記憶體 來存放 c/c++ run-time 需要的全區域變數

配置 Thread 專用記憶體 來存放 c/c++ run-time 需要的全區域變數

Step 1Step 1Step 2Step 2

Step 3Step 3

幫你把 CreateThread 包起來了幫你把 CreateThread 包起來了

繼續執行繼續執行

Page 33: Thread Basics

_beginthreadex _beginthreadex 虛擬碼虛擬碼unsigned long __cdecl _beginthreadex( … ){ _ptiddata ptd; // thread’s data block 指標

// 配置 data block 給新的 thread if ((ptd = _calloc_crt(1, sizeof(struct tiddata))) = = NULL) goto error_return; … // 將 callback function 的位址與相關參數 ptd 中 ptd->_initaddr = (void *) pfnStartAddr; ptd->_initarg = pvParam; // 建立新的 Thread thdl = (unsigned long) CreateThread(psa, cbStack, _threadstartex, (PVOID) ptd, fdwCreate, pdwThreadID); return (thdl); error_return: _free_ctr(ptd); return ((unsigned long ) 0);}

呼叫 _threadstartex呼叫 _threadstartex

配置 Thread 專用記憶體 來存放 c/c++ run-time 會存取的 全區域變數

配置 Thread 專用記憶體 來存放 c/c++ run-time 會存取的 全區域變數

發生問題時的處理程式碼發生問題時的處理程式碼 接下來 , 看看_threadstartex 怎

麼做的 ?

主要的工作 : 就是 配置 專用空間 與 建立 thread主要的工作 : 就是 配置 專用空間 與 建立 thread

1

2

Page 34: Thread Basics

_thread_threadstartexstartex 虛擬碼虛擬碼static unsigned long WINAPI threadstartex (void* ptd){ // 將 tiddata block 指定給目前新建立的 thread TlsSetValue(__tlsindex, ptd);

// 將目前 thread id 存起來 ((_ptiddata) ptd)->_tid = GetCurrentThreadId( ); … _try{ _endthreadex(( (unsigned (WINAPI *)(void *)) (((_ptiddata)ptd)->_initaddr) ) ( ((_ptiddata)ptd)->_initarg ) ) ; }__except( … ){ _exit(GetExceptionCode()); } return(0L);} 最後呼叫 _endthreadex,釋放記憶體最後呼叫 _endthreadex,釋放記憶體

Structure Exception Handle 是用來防止你的程式發生 exception 或用來處理 C 的 signal function 用的

將配置好的空間 , associate 給目前的 thread

將配置好的空間 , associate 給目前的 thread

這樣 , 每個 thread 就有自己的空間存放 global variable 了

這樣 , 每個 thread 就有自己的空間存放 global variable 了

呼叫使用者指定的thread function

呼叫使用者指定的thread function

指定參數指定參數

主要的工作 : 就是 分配 專用空間 與 呼叫 thread function主要的工作 : 就是 分配 專用空間 與 呼叫 thread function

1

2

Page 35: Thread Basics

_endthread _endthread 虛擬碼虛擬碼void __cdecl _endthreadex (unsigned retcode) { _ptiddata ptd;

// 取得 tiddata 位址 ptd = _getptd( );

// Free the tiddata block. _freeptd(ptd);

// Terminate the thread. ExitThread(retcode);}

當 thread 結束時 , 釋放存放 c/c++ run-timeLibrary 變數的空間

當 thread 結束時 , 釋放存放 c/c++ run-timeLibrary 變數的空間

主要的工作 : 就是 free 專用空間主要的工作 : 就是 free 專用空間

Page 36: Thread Basics

於是當你執行 於是當你執行 int d=errorno; int d=errorno; 時時

經由下面的巨集轉換為 經由下面的巨集轉換為 function call function call 的的returnreturn

#if defined(_MT) || defined(_DLL) extern int * __cdecl _errno(void); #define errno (*_errno( ))#else /* ndef _MT && ndef _DLL */ extern int errno;#endif /* _MT || _DLL */

定義處理 errorno 的 function定義處理 errorno 的 function

Errno 呼叫 _errno 函式 , 並且得 到位址的內容

Errno 呼叫 _errno 函式 , 並且得 到位址的內容

如果你定義 multithread

Page 37: Thread Basics

_beginthread _beginthread 範例範例#include <windows.h>

#include <process.h> /* _beginthread, _endthread */#include <conio.h> //_getch

#include <iostream>using namespace std;

void loop_fun(void* ch); // 印出指定的字元void CheckKey( void *dummy ); // 檢查否要離開程式

BOOL repeat = TRUE; int main() { char data='_';

_beginthread( loop_fun, 0,(void*)data); // ch 指標變數因為是 void* 型態 , 所以要先 cast

_beginthread( loop_fun, 0,(void*)'*');

// 建立一個 thread, 隨時檢查使用者是否要終止程式 ( 藉由 repeat 控制 )

_beginthread( CheckKey, 0, NULL );

// 等待結束while( repeat ){ }

}

建立第一條 thread

建立第二條 thread

建立第三條 thread

1

2

Page 38: Thread Basics

// 印出指定的字元void loop_fun(void *ch){ char data=(char)ch; while(repeat){

cout << data << " "; Sleep( 100L ); }}

// 隨時檢查使用者是否要終止程式void CheckKey( void *dummy ){ _getch(); // 等待使用者鍵入任意字元 (不會秀出字來 ) repeat = 0; }

Page 39: Thread Basics

RemarkRemark問題一 :你指的是 標題為 : "_threadstartex 虛擬碼 " 的投影片嗎 ?這頁投影片是把 C/C++ run-time 的原始程式秀出來 , 也就是那個 _endthreadex 所以你才不用 呼叫 .嗯 .. 這個我在上課時 , 就懷疑應該是不用呼叫 _endthread()但是你也可以明確的呼叫 , 不過這只是確定一下而已 .MSDN

問題二 : 當你建立 thread 時 , 還記得 thread 的 kernel object 的 usage count 會增加 2 嗎 ?一個是自己 另一個就是 primary thread,  所以當你在 primary thread 明確的呼叫CloseHandle( (HANDLE) x), 這會把 x 這個 thread 的 kernerl object usage count -1 ( 這時 x thread kernel object usage count 由 2 變成 1),  當 x 的 thread function 結束時 , 系統會自動加上 _endthread(). 並且自動 CloseHandle().  此時 , thread 的 kernel object usage count 由 1 變成 0, 系統把這個 thread object 回收 . 

Page 40: Thread Basics

--  注意事項 : (注意 : 因為你使用 _beginthread() 建立 thread, 所以 Win32 會在 呼叫 _endthread() 結束前 , 自動 Close 該 thread 的 Handle,              若你用 _beginthreadex() 建立 thread, 則系統不會幫你呼叫 CloseHandle)請參閱 MSDN :  http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vccore98/html/_crt__beginthread.2c_._beginthreadex.asp 其中的 Remark 區段就可以了 .

Page 41: Thread Basics

EndEnd