Thread Basics
description
Transcript of Thread Basics
Thread BasicsThread 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
IntroductionIntroduction
Process Process 本身是靜止不動的本身是靜止不動的 一個行程一個行程從不執行任何程式碼從不執行任何程式碼,它只是一,它只是一
個 個 thread thread 的的容器容器 Thread Thread 總是在某個行程的「執行內容總是在某個行程的「執行內容(( contextcontext )」中被建立,並且在該行程)」中被建立,並且在該行程中度過它們的一生 中度過它們的一生
當 thread function 執行結束則 thread 生命也就結束
當 thread function 執行結束則 thread 生命也就結束
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
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 執行的流程
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 自動消滅
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
馬上寫個範例馬上寫個範例
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
執行的過程執行的過程
馬上寫個範例馬上寫個範例
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 中
系統建立 系統建立 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()
先暫停先暫停
呼叫呼叫
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
接下來看看接下來看看 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
啟動 啟動 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 生命
我們都知道 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
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
請使用這個請使用這個
不要使用不要使用不要使用不要使用
不要使用不要使用
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 的比較
當 當 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 ( 攔截 )
我要如何知道 我要如何知道 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
如果你有用到 如果你有用到 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
請使用 請使用 _beginthreadex_beginthreadex
直接替換會有問題直接替換會有問題 !!
hThread=CreateThread(NULL,0,FirstThread,(PVOID)Data,0,&dwThreadID);
hThread=_beginthreadex(NULL,0,FirstThread,(PVOID)Data,0,&dwThreadID);
你應該要轉型才對 !
請使用 請使用 _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
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
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
列印出的位址
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 的設定
Visual Studio .Net Visual Studio .Net 設定設定
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 的情況
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 程式片段
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( ))
程式設計技巧程式設計技巧 經全區域變數的存取經全區域變數的存取轉換轉換成內部成內部 變數存取 變數存取
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 值
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 的詳細實作情形了
來看看為甚麼 來看看為甚麼 _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
三個三個 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 包起來了
繼續執行繼續執行
_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
_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
_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 專用空間
於是當你執行 於是當你執行 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
_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
// 印出指定的字元void loop_fun(void *ch){ char data=(char)ch; while(repeat){
cout << data << " "; Sleep( 100L ); }}
// 隨時檢查使用者是否要終止程式void CheckKey( void *dummy ){ _getch(); // 等待使用者鍵入任意字元 (不會秀出字來 ) repeat = 0; }
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 回收 .
-- 注意事項 : (注意 : 因為你使用 _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 區段就可以了 .
EndEnd