介紹
在現(xiàn)代操作系統(tǒng)中,每個(gè)進(jìn)程都有自己的地址空間和一個(gè)控制線程。然而,在實(shí)踐中,我們經(jīng)常面臨需要在單個(gè)進(jìn)程中執(zhí)行多個(gè)并發(fā)任務(wù)并訪問相同流程組件的情況:結(jié)構(gòu)、打開文件描述符等。
在任何情況下組織多線程模型都需要同時(shí)訪問相同的資源。本文提供有關(guān) Windows 和 Linux 操作系統(tǒng)中線程的一般信息,然后介紹同步機(jī)制[1]阻止訪問共享資源。
對(duì)于那些處理從一個(gè)系統(tǒng)移植到另一個(gè)系統(tǒng)的應(yīng)用程序,或者在一個(gè)系統(tǒng)中創(chuàng)建多線程應(yīng)用程序并想知道它在另一個(gè)系統(tǒng)中的實(shí)際實(shí)現(xiàn)方式的人來說,本文將很有趣。對(duì)于那些從未使用多個(gè)線程編寫應(yīng)用程序但計(jì)劃在未來這樣做的人,本文也將很有用。
螺紋概念
這些線程需要做什么?為什么我們不能只創(chuàng)建流程?后一種范式已經(jīng)工作了很多年,但流程創(chuàng)建有一些缺點(diǎn),只有以下幾個(gè)例子:
流程創(chuàng)建操作是資源密集型操作。
進(jìn)程需要復(fù)雜的機(jī)制來訪問相同的資源(命名或未命名管道、消息隊(duì)列、套接字等),而線程會(huì)自動(dòng)訪問相同的地址空間。
多線程進(jìn)程的性能高于單線程進(jìn)程。
多線程允許多個(gè)線程作為一個(gè)進(jìn)程的一部分執(zhí)行。帶有線程的編程模型為開發(fā)人員提供了同時(shí)執(zhí)行的舒適抽象。帶線程的程序的優(yōu)點(diǎn)之一是它在具有多核處理器的計(jì)算機(jī)上運(yùn)行得更快。線程在創(chuàng)建時(shí)幾乎不使用資源,也不使用資源訪問機(jī)制等其他插件;此外,線程的性能和應(yīng)用程序交互性更高。除了地址空間,所有線程都使用:
工藝規(guī)定
信號(hào)處理程序(用于處理信號(hào)的設(shè)置)
當(dāng)前目錄
用戶和組標(biāo)識(shí)符
同時(shí),每個(gè)線程都有自己的:
線程標(biāo)識(shí)符
疊
寄存器集
信號(hào)掩模
優(yōu)先權(quán)
使用線程的主要函數(shù)
在通過 exec 調(diào)用啟動(dòng)程序時(shí),將創(chuàng)建一個(gè)主線程(初始線程)。輔助線程是通過調(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
);
/* 成功完成時(shí)返回 0,出錯(cuò)時(shí)返回正值*/
每個(gè)線程都有其標(biāo)識(shí)符 –pthread_t– 和屬性:優(yōu)先級(jí)、初始堆棧大小、守護(hù)程序功能。創(chuàng)建線程時(shí),必須指示將執(zhí)行的函數(shù)地址 (func) 以及單指針參數(shù) (arg)。Linux 中的線程應(yīng)顯式退出 –pthread_exit通過調(diào)用函數(shù) – 或間接退出 – 通過從該函數(shù)返回[2]。如果在問題條件下需要將多個(gè)參數(shù)傳遞給線程,則必須將結(jié)構(gòu)的地址與參數(shù)一起使用。
在Windows中,線程是在_beginthread(ex)或CreateThread函數(shù)的幫助下創(chuàng)建的。兩者都是運(yùn)行時(shí)調(diào)用,它們之間的主要區(qū)別在于 CreateThread 是一個(gè)“原始”Win32 API,并且_beginthread(ex)反過來調(diào)用自身內(nèi)部的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, in Windows:security– 指向結(jié)構(gòu)的指針SECURITY_ATTRIBUTES,thrdaddr– 指向接收線程標(biāo)識(shí)符的 32 位變量。
讓我們考慮以下線程創(chuàng)建示例[3]:
#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 threadn”);
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 threadn”);
return 0;
}
輸出將如下所示:
Linux目錄窗戶
[root@localhost~]# 。/進(jìn)程
來自主線程的你好
[root@localhost ~]#C:》進(jìn)程.exe
來自主線程的你好
C:》
很容易注意到process_command_thread不是以可視方式運(yùn)行的。當(dāng)用于線程管理的內(nèi)部結(jié)構(gòu)由pthread_createor_beginthreadex函數(shù)初始化時(shí),主線程完成執(zhí)行。我們可以預(yù)期在為 Linux 調(diào)用pthread_join后線程退出。
int pthread_join(pthread_t tid, void **retval);
線程可以是可連接的(默認(rèn)情況下)或分離的。當(dāng)可連接線程終止時(shí),信息(標(biāo)識(shí)符、終止?fàn)顟B(tài)、線程計(jì)數(shù)器等)將保留,直到調(diào)用pthread_join。
在Windows操作系統(tǒng)中,其中一個(gè)等待功能可能被視為類似于pthread_join。等待函數(shù)系列允許線程中斷其執(zhí)行并等待資源釋放。讓我們看一下pthread_join的類似物,即WaitForSingleObject:
DWORD WaitForSingleObject(HANDLE hObject, DWORD dwMilliseconds);
調(diào)用此函數(shù)時(shí),第一個(gè)參數(shù) hObject 標(biāo)識(shí)內(nèi)核對(duì)象。此對(duì)象可能處于以下兩種狀態(tài)之一:“空閑”或“忙碌”。
第二個(gè)參數(shù) dwMilliseconds 指示線程準(zhǔn)備好等待釋放對(duì)象的毫秒數(shù)。
以下示例說明了pthread_joinWaitForSingleObject調(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__
對(duì)于 Linux,本文介紹了由 POSIX.1-2001 標(biāo)準(zhǔn)(稱為“pthreads”)定義的線程接口。
本文稍后將介紹線程退出。
在此示例中,與本文中的其他示例一樣,Linux 和 Windows 的代碼庫都是單一的。區(qū)別在于編譯條件:
#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文章
11329瀏覽量
209972 -
WINDOWS
+關(guān)注
關(guān)注
4文章
3554瀏覽量
89008
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論