在 Windows 代碼庫(kù)中,有一個(gè)常量INFINITE,它由第二個(gè)參數(shù)傳遞。此常量指示線程無(wú)限期地等待事件。常量在WinBase.h中聲明,定義為0xFFFFFFFF(或 -1)。
此外,Windows代碼還包括WAIT_TIMEOUT。此條件在 Linux 中沒有表示。實(shí)際上,借助以下功能可以繞過(guò)此限制:
int pthread_tryjoin_np(pthread_t thread, void **retval);
int pthread_timedjoin_np(
pthread_t thread,
void **retval,
const struct timespec *abstime
);
如果您參考pthread_tryjoin_np幫助頁(yè)面,您可以看到EBUSY可能是一個(gè)錯(cuò)誤,并且 WaitForSingleObject無(wú)法通知我們。要了解線程的狀態(tài)并識(shí)別其退出代碼,必須調(diào)用該函數(shù):
BOOL GetExitCodeThread(HANDLE hThread, PDWORD pdwExitCode);
退出代碼作為pdwExitCode指向的變量返回。如果在調(diào)用函數(shù)時(shí)線程尚未終止,則STILL_ACTIVE標(biāo)識(shí)符將填充為變量。如果調(diào)用成功,則函數(shù)返回TRUE。
讓我們考慮一個(gè) Linux 的pthread_tryjoin_np函數(shù)用法和Windows 的 GetExitCodeThreadWaitForSingleObject函數(shù)的情況。
#ifdef __PL_WINDOWS__
DWORD dwret;
BOOL bret;
DWORD h_process_command_thread_exit_code;
if (h_process_command_thread != NULL) {
bret = GetExitCodeThread(
h_process_command_thread,
&h_process_command_thread_exit_code
);
if (h_process_command_thread_exit_code == STILL_ACTIVE) {
dwret = WaitForSingleObject(
h_process_command_thread,
5000 // 5000ms
);
switch (dwret) {
case WAIT_OBJECT_0:
// everything from this point on is good break;
case WAIT_TIMEOUT:
case WAIT_FAILED:
default:
SetLastError(dwret);
break;
}
}
}
#endif //__PL_WINDOWS__
#ifdef __PL_LINUX__
int iret;
struct timespec wait_time = { 0 };
if (h_process_command_thread_initialized == 1) {
iret = pthread_tryjoin_np(
h_process_command_thread,
NULL
);
if ((iret != 0) && (iret != EBUSY)) {
//TODO: process the error
}
if (iret == EBUSY) {
clock_gettime(CLOCK_REALTIME, &wait_time);
ADD_MS_TO_TIMESPEC(wait_time, 5000);
iret = pthread_timedjoin_np(
h_process_command_thread,
NULL,
&wait_time
);
switch (iret) {
case 0:
// everything from this point on is good
break;
case ETIMEDOUT:
case EINVAL:
default:
break;
}
}
}
#endif //__PL_LINUX__
細(xì)心的讀者會(huì)注意到ADD_MS_TO_TIMESPEC是 Linux 操作系統(tǒng)中未表示的宏。 宏被添加到wait_time5000毫秒,但宏實(shí)現(xiàn)不在本文的討論范圍之內(nèi)。還應(yīng)該提到的是,在 Linux 中我們需要引入一個(gè)單獨(dú)的變量h_process_command_thread_initialized,因?yàn)閜thread_t是無(wú)符號(hào)的 long(一般來(lái)說(shuō)),我們無(wú)法驗(yàn)證它。
讓我們總結(jié)一下結(jié)果。Linux 和 Windows 操作系統(tǒng)提供了在應(yīng)用程序內(nèi)部創(chuàng)建線程的機(jī)會(huì)。在Windows操作系統(tǒng)中,類型是HANDLE和Linux-pthread_t。如果在 Linux 操作系統(tǒng)中創(chuàng)建可連接線程,即使我們確定線程已終止,也有必要編寫pthread_join。這種做法將幫助我們避免系統(tǒng)資源泄漏。
討論的功能記錄在表 1 中。
Linux函數(shù) | 窗口函數(shù) |
pthread_create | 開始線程 |
pthread_join | WaitForSingleObject(.., INFINITE) |
pthread_timedjoin_np | GetExitCodeThreadWaitForSingleObject |
pthread_tryjoin_np | 獲取退出代碼線程 |
表 1.Windows 和 Linux 操作系統(tǒng)中的線程同步函數(shù)。
事件
事件是內(nèi)核對(duì)象變體的實(shí)例。事件通知操作終止,通常在線程執(zhí)行初始化,然后向另一個(gè)線程發(fā)出可以繼續(xù)工作的信號(hào)時(shí)使用。初始化線程將 ?event? 對(duì)象轉(zhuǎn)換為無(wú)信號(hào)狀態(tài),然后繼續(xù)其操作。完成后,它將事件釋放到信號(hào)狀態(tài)。反過(guò)來(lái),一直在等待事件將其狀態(tài)更改為信號(hào)的另一個(gè)線程恢復(fù)并再次成為計(jì)劃線程。
讓我們來(lái)看看在Windows和Linux操作系統(tǒng)中處理“事件”對(duì)象的函數(shù)。
在Windows操作系統(tǒng)中,使用CreateEvent函數(shù)創(chuàng)建一個(gè)“事件”對(duì)象:
HANDLE CreateEvent(
PSECURITY_ATTRIBUTES psa,
BOOL fManualReset,
BOOL fInitialState,
PCSTR pszName
);
讓我們更清楚地關(guān)注fManualReset和fInitialState參數(shù)。BOOL類型的FManualReset參數(shù)通知系統(tǒng)需要?jiǎng)?chuàng)建手動(dòng)重置事件 (TRUE)或自動(dòng)重置事件 (FALSE)。fInitialState參數(shù)確定事件的初始狀態(tài):已發(fā)出信號(hào) (TRUE)或未發(fā)出信號(hào) (FALSE)。
創(chuàng)建事件后,可以管理狀態(tài)。要將事件轉(zhuǎn)換為信號(hào)狀態(tài),您需要調(diào)用:
Bool SetEvent(HANDLE hEvent);
若要將事件狀態(tài)更改為無(wú)信號(hào),需要調(diào)用:
Bool ResetEvent(HANDLE hEvent);
要等待事件信號(hào),您需要使用我們已經(jīng)熟悉的WaitForSingleObject函數(shù)。
在Linux操作系統(tǒng)中,“事件”對(duì)象表示整數(shù)描述符。一個(gè)整數(shù) ?event? 對(duì)象是使用eventfd函數(shù)創(chuàng)建的:
int eventfd(unsigned int initval, int flags);
initval參數(shù)是一個(gè)內(nèi)核服務(wù)計(jì)數(shù)器。flags參數(shù)是eventfd行為修改所必需的,可以是EFD_CLOEXEC、EFD_NONBLOCK或EFD_SEMAPHORE。如果成功終止,eventfd將返回一個(gè)新的文件描述符,該描述符可用于鏈接eventfd對(duì)象。
與SetEvent類似,我們可以使用eventfd_write調(diào)用:
ssize_t eventfd_write(int fd, const void *buf, size_t count);
從緩沖區(qū)調(diào)用寫入時(shí),會(huì)將 8 字節(jié)整數(shù)值添加到計(jì)數(shù)器中。最大計(jì)數(shù)器值可以是 64 位無(wú)符號(hào)減 1。如果函數(shù)調(diào)用成功,則返回寫入的字節(jié)數(shù)。
在我們討論ResetEvent類似物之前,讓我們看一下輪詢函數(shù)。
#include
int poll(struct pollfd fdarray[], nfds_t nfds, int timeout);
輪詢功能允許應(yīng)用程序同時(shí)阻止多個(gè)描述符,并在其中任何一個(gè)準(zhǔn)備好讀取或?qū)懭霑r(shí)立即接收通知。民意調(diào)查工作(一般)可以描述如下:
當(dāng)任何描述符準(zhǔn)備好進(jìn)行輸入-輸出操作時(shí)發(fā)出通知。
如果沒有任何描述符準(zhǔn)備就緒,請(qǐng)進(jìn)入睡眠模式,直到一個(gè)或多個(gè)描述符準(zhǔn)備就緒。
如果有可用的描述符準(zhǔn)備用于輸入-輸出,請(qǐng)?zhí)幚硭鼈兌粫?huì)阻塞。
返回到步驟 1。
Linux 操作系統(tǒng)為多路復(fù)用輸入輸出提供了三個(gè)實(shí)體:用于選擇(選擇)、輪詢(輪詢)、擴(kuò)展輪詢(epoll)的接口。
那些有使用select經(jīng)驗(yàn)的人可能會(huì)欣賞投票的優(yōu)勢(shì),它使用更有效的方法,基于位掩碼使用三組描述符。輪詢調(diào)用適用于文件描述符指向的單個(gè)nfdspollfd結(jié)構(gòu)數(shù)組。
讓我們看一下pollfd結(jié)構(gòu)定義:
struct pollfd {
int fd; /* file descriptor */
short events; /* requested events */
short revents; /* returned events */
};
每個(gè)pollfd結(jié)構(gòu)中都指示了一個(gè)將被跟蹤的文件描述符??梢詫⒍鄠€(gè)文件描述符傳遞給輪詢函數(shù)(結(jié)構(gòu)的 pollfd數(shù)組)。fdarray數(shù)組中的元素?cái)?shù)由nfds參數(shù)確定。
為了將我們感興趣的事件傳達(dá)給內(nèi)核,有必要在數(shù)組中每個(gè)元素的事件字段中寫入表 2 中的一個(gè)或多個(gè)值。從輪詢函數(shù)返回后,內(nèi)核指定每個(gè)描述符發(fā)生的事件。
名字 | 事件 | 活動(dòng) | 描述 |
波林 | + | + | 數(shù)據(jù)可供讀?。ǜ邇?yōu)先級(jí)除外) |
波爾德規(guī)范 | + | + | 常規(guī)數(shù)據(jù)(優(yōu)先級(jí) 0)可供讀取 |
波爾德班德 | + | + | 具有非零優(yōu)先級(jí)的數(shù)據(jù)可供讀取 |
波普里普里 | + | + | 高優(yōu)先級(jí)數(shù)據(jù)可供讀取 |
波羅特 | + | + | 數(shù)據(jù)可供寫入 |
波爾沃諾姆 | + | + | 類似于 波勞特 |
民意調(diào)查帶 | + | + | 具有非零優(yōu)先級(jí)的數(shù)據(jù)可用于寫入 |
波勒爾 | + | 發(fā)生錯(cuò)誤 | |
波爾赫普 | + | 連接丟失 | |
波倫瓦爾 | + | 描述符和打開的文件不匹配 |
表 2.事件和輪詢函數(shù)的revents標(biāo)志的可能值。
參數(shù)超時(shí)定義發(fā)生指定事件的等待時(shí)間。超時(shí)有三種可能的值。
timeout= -1:等待時(shí)間是無(wú)限的(在 WaitForSingleObject中為 INFINITE)。
timeout= 0:等待時(shí)間等于 0,表示需要檢查所有指定的描述符并將控制權(quán)交還給調(diào)用程序。
超時(shí)> 0:等待時(shí)間不超過(guò)超時(shí)毫秒。
在查看了輪詢函數(shù)之后,我們可以得出結(jié)論,在Windows操作系統(tǒng)中,“事件”對(duì)象與WaitForSingleObject類似。
讓我們轉(zhuǎn)到 Linux 的ResetEvent類似物。
#ifdef __PL_LINUX__
struct pollfd wait_object;
uint64_t event_value;
int ret;
if (eventfd_descriptor > 0) { // Descriptor created by eventfd(0,0)
wait_object.fd = eventfd_descriptor;
wait_object.events = POLLIN;
wait_object.revents = 0;
ret = poll(&wait_object, 1, 0); // Do not wait
if (ret < 0) { // Error
} else {
if ((wait_object.revents & POLLIN) != 0) {
iret = eventfd_read(eventfd_descriptor, &event_value);
if (iret != 0) { // Error }
}
}
}
#endif //__PL_LINUX__
最初我們檢查eventfd_descriptor是否大于零[2](實(shí)際上,這最初是由eventfd函數(shù)創(chuàng)建的,沒有錯(cuò)誤)。之后,我們初始化pollfd函數(shù)并運(yùn)行輪詢。需要執(zhí)行輪詢以檢查是否有可用的數(shù)據(jù)可供讀取。如果有此類數(shù)據(jù),我們將讀取它。
通過(guò)上述所有內(nèi)容的鏡頭,讓我們反映表3中的結(jié)果:
窗口函數(shù) | Linux函數(shù) |
創(chuàng)建事件 | 事件FD |
設(shè)置事件 | eventfd_write |
重置事件 | 投票/eventfd_read |
等待單個(gè)對(duì)象 | 民意調(diào)查 |
表 3.用于處理 Windows 中的事件及其在 Linux 中的類似事件的主要函數(shù)。
審核編輯:郭婷
-
Linux
+關(guān)注
關(guān)注
87文章
11182瀏覽量
208519 -
WINDOWS
+關(guān)注
關(guān)注
3文章
3510瀏覽量
88230
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論