介紹
在現(xiàn)代操作系統(tǒng)中,每個進(jìn)程都有自己的地址空間和一個控制線程。然而,在實踐中,我們經(jīng)常遇到需要在單個進(jìn)程中執(zhí)行多個并發(fā)任務(wù)并訪問相同進(jìn)程組件的情況:結(jié)構(gòu)、打開的文件描述符等。
在任何情況下組織多線程模型都需要同時訪問相同的資源。本文提供了有關(guān) Windows 和 Linux 操作系統(tǒng)中線程的一般信息,然后介紹了防止訪問共享資源的同步機(jī)制[1] 。
對于那些處理從一個系統(tǒng)移植到另一個系統(tǒng)的應(yīng)用程序或在一個系統(tǒng)中創(chuàng)建多線程應(yīng)用程序并想知道它在另一個系統(tǒng)中如何實際實現(xiàn)的人來說,這篇文章將會很有趣。本文對于那些從未編寫過多線程應(yīng)用程序但計劃在未來這樣做的人也很有用。
線程概念
這些線程是做什么用的?為什么我們不能只創(chuàng)建流程?后一種范式已經(jīng)工作了很多年,但是流程創(chuàng)建有一些缺點,下面舉幾個例子:
進(jìn)程創(chuàng)建操作是資源密集型的。
進(jìn)程需要復(fù)雜的機(jī)制來訪問相同的資源(命名或未命名的管道、消息隊列、套接字等),而線程會自動獲得對相同地址空間的訪問權(quán)。
多線程進(jìn)程的性能高于單線程。
多線程允許多個線程作為一個進(jìn)程的一部分執(zhí)行。具有線程的編程模型為開發(fā)人員提供了對同時執(zhí)行的舒適抽象。具有線程的程序的優(yōu)點之一是它在具有多核處理器的計算機(jī)上運(yùn)行得更快。線程在創(chuàng)建時幾乎不使用資源,或者額外的插件,例如資源訪問機(jī)制;此外,線程的性能和應(yīng)用程序交互性更高。除了地址空間,所有線程都使用:
工藝規(guī)定
信號處理程序(處理信號的設(shè)置)
當(dāng)前目錄
用戶和組標(biāo)識符
同時,每個線程都有自己的:
線程標(biāo)識符
堆
寄存器集
信號屏蔽
優(yōu)先
使用線程的主要函數(shù)
在通過 exec 調(diào)用啟動程序時,會創(chuàng)建一個主線程(初始線程)。輔助線程是通過調(diào)用 Linux 的 pthread_create 或 Windows 的 _beginthread(ex) 創(chuàng)建的。
讓我們更仔細(xì)地看看 Linux 的線程創(chuàng)建:
#include
int pthread_create(
pthread_t *tid,
const pthread_attr_t *attr,
void *(*func)(void *),
void *arg
);
/* Returns 0 in case of a successful completion, positive value in case of an error*/
每個線程都有它的標(biāo)識符——pthread_t——和屬性:優(yōu)先級、初始堆棧大小、守護(hù)進(jìn)程特性。創(chuàng)建線程時,需要指明將要執(zhí)行的函數(shù)地址(func),以及單指針參數(shù)(arg)。Linux 中的線程應(yīng)顯式退出——通過調(diào)用pthread_exit函數(shù)——或隱式退出——通過從該函數(shù)返回[2]。如果在問題的條件下需要將多個參數(shù)傳遞給線程,則必須使用帶參數(shù)的結(jié)構(gòu)地址。
在 Windows 中,線程是在_beginthread(ex)或CreateThread函數(shù)的幫助下創(chuàng)建的。兩者都是 ?-runtime 調(diào)用,它們之間的主要區(qū)別在于CreateThread是一個“原始”Win32 API,而_beginthread(ex)在其內(nèi)部調(diào)用CreateThread 。在本文中,我們將討論_beginthread(ex)函數(shù)。_beginthreadex的語法如下:
uintptr_t _beginthreadex(
void *security,
unsigned stack_size,
unsigned(__stdcall *start_address)(void *),
void *arglist,
unsigned initflag,
unsigned *thrdaddr
);
可以觀察到pthread_create和_beginthreadex調(diào)用之間有一些模糊的相似性;但是,也存在差異。?hus,在 Windows 中:security– 指向SECURITY_ATTRIBUTES結(jié)構(gòu)的指針,thrdaddr– 指向接收線程標(biāo)識符的 32 位變量。
讓我們考慮以下線程創(chuàng)建示例:
#include
#ifdef __PL_WINDOWS__
#include
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
#include
#endif //__PL_LINUX__
#define STACK_SIZE_IN_BYTES (2097152) //2MB
#ifdef __PL_WINDOWS__
unsigned int __stdcall process_command_thread(void) {
#endif //__PL_WINDOWS__
#if defined (__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
void *process_command_thread(void *p) {
#endif //(__PL_LINUX__) || (__PL_SOLARIS__) || (__PL_MACOSX__)
printf("Hello from process command thread\n");
return 0;
}
int main(int argc, char *argv[])
{
#ifdef __PL_WINDOWS__
DWORD process_command_thread_id;
HANDLE h_process_command_thread;
h_process_command_thread = (HANDLE)_beginthreadex(
NULL,
STACK_SIZE_IN_BYTES,
process_command_thread,
NULL,
0,
(unsigned long *)&process_command_thread_id
);
if (h_process_command_thread == NULL)
return -1;
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
pthread_t h_process_command_thread;
int h_process_command_thread_initialized;
int ret;
ret = pthread_create(
&h_process_command_thread,
NULL,
process_command_thread,
NULL
);
if (ret != 0)
return -1;
h_process_command_thread_initialized = 1;
#endif // __PL_LINUX__
printf("Hello from main thread\n");
return 0;
}
輸出將如下:
很容易注意到 process_command_thread 沒有以可視方式運(yùn)行。當(dāng)用于線程管理的內(nèi)部結(jié)構(gòu)被pthread_create或_beginthreadex函數(shù)初始化時,主線程完成執(zhí)行。在 Linux 中調(diào)用 pthread_join 后,我們可以期待線程退出。
int pthread_join(pthread_t tid, void **retval);
線程可以是可連接的(默認(rèn)情況下)或分離的。當(dāng)一個可連接線程終止時,信息(標(biāo)識符、終止?fàn)顟B(tài)、線程計數(shù)器等)會一直保存到調(diào)用pthread_join為止。
在 Windows 操作系統(tǒng)中,可以認(rèn)為等待函數(shù)之一類似于pthread_join。等待函數(shù)系列允許線程中斷其執(zhí)行并等待資源被釋放。讓我們看一下pthread_join的類似物,即WaitForSingleObject:
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
調(diào)用此函數(shù)時,第一個參數(shù)hObject標(biāo)識內(nèi)核對象。該對象可能處于以下兩種狀態(tài)之一:“空閑”或“忙碌”。
第二個參數(shù)dwMilliseconds表示線程準(zhǔn)備等待釋放對象的毫秒數(shù)。
以下示例說明了pthread_join\WaitForSingleObject調(diào)用:
#ifdef __PL_WINDOWS__
DWORD status = WaitForSingleObject(
h_process_command_thread,
INFINITE
);
switch (status) {
case WAIT_OBJECT_0:
// The process terminated
break;
case WAIT_TIMEOUT:
// The process did not terminate within timeout
break;
case WAIT_FAILED:
// Bad call to function
break;
}
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
int status = pthread_join(
h_process_command_thread,
NULL
);
switch (status) {
case 0:
// The process terminated
break;
case default:
// Bad call to function
break;
}
#endif //__PL_LINUX__
#ifdef __PL_WINDOWS__
//Windows code
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
//Code for UNIX OS systems
#endif //__PL_LINUX__
審核編輯:郭婷
-
Linux
+關(guān)注
關(guān)注
87文章
11345瀏覽量
210414 -
WINDOWS
+關(guān)注
關(guān)注
4文章
3570瀏覽量
89312 -
操作系統(tǒng)
+關(guān)注
關(guān)注
37文章
6896瀏覽量
123756
發(fā)布評論請先 登錄
相關(guān)推薦
評論