![](/img/trans.png)
[英]Synchronization issue with usage of pthread_kill() to terminate thread blocked for I/O
[英]pthread_kill() vs pthread_cancel() to terminate a thread blocked for I/O
在我們的服務器代碼中,我們使用poll()系統調用來監視客戶端套接字。 以較大的超時值調用poll()。 因此,調用poll()的線程被阻止進行I / O。
根據流程,我們有一個場景,我們需要終止來自另一個線程的poll()中阻塞的線程。 我遇到過pthread_kill()和pthread_cancel()函數,它們可以終止為I / O阻塞的目標線程。
通過閱讀手冊頁,這兩個功能似乎都能正常工作。 互聯網上很少有鏈接表明這兩種功能都使用起來很危險。
是否有其他方法可以終止為I / O阻塞的線程? 如果沒有,建議使用以下哪個功能。
根據線程庫的確切實現,線程被殺死時很可能甚至不會從poll
返回-因此,您甚至可能無法實現所需的功能。
您需要非常小心,不要造成內存泄漏,並且仍然很可能通過殺死擁有它的線程來創建文件描述符泄漏(請注意,與進程相反,線程資源不會被進程“清理”)系統)。
通常,使用較短的超時時間並輪詢其間的終止標志,或者使用信號來中斷系統調用,然后在其自己的控制下終止線程,以釋放所有分配的資源,通常更為安全。
一個簡單而干凈的選擇是創建一個“信號”管道。 也就是說,調用pipe
,獲取“ read”端的文件描述符,並將其添加到poll
文件描述符列表中(使用POLLIN
)。 然后,每當您要解除阻塞在poll
等待的線程時,只需將一個字節寫入管道的寫入端即可。 接收到數據的管道將在阻塞線程中以可讀形式返回。 您甚至可以通過更改寫入的字節的值來指定不同的“命令”。
(您當然需要從管道中讀取該字節,然后才能重新使用它。)
沒有殺死線程這樣的事情。
命名不佳的pthread_kill
函數是命名為kill
函數的線程類似物,后者將信號發送給進程。 從歷史上講, kill
含義是合理的,因為許多信號的默認操作是殺死進程。 但是,殺死進程的默認操作並不取決於信號是發送到進程還是特定線程 —不管哪種方式,進程都會終止。
只有當您想在另一個線程上調用信號處理程序時, pthread_kill
才有用。 除非您確定信號處理程序無法中斷任何非異步信號安全的函數,否則信號處理程序僅限於調用異步信號安全的函數,因此甚至無法采取行動來終止線程的生存期( pthread_exit
不是異步信號安全的)。
如果您對線程最終因調用而終止感到滿意,那么pthread_cancel
是結束阻塞在阻塞操作中的線程的正確方法。 但是,為了安全地使用它,您需要大量使用pthread_cleanup_push
和pthread_cleanup_pop
。
如果您不希望線程終止,則信號是您唯一的選擇。 您有兩種選擇:
在不使用SA_RESTART
情況下使用sigaction
安裝信號處理程序(可以是無操作),這樣會導致EINTR
。 由於這種方法存在固有的競爭條件(如果您在進入阻塞的系統調用之前發送信號,而不是一旦被阻塞,則信號將不會執行任何操作),因此您需要重復發送帶有指數補償的信號為了避免餓死目標的執行時間,直到目標通過某種其他同步機制(POSIX信號燈工作正常)確認收到消息為止。
安裝一個信號處理程序,它將longjmp
。 為了安全地執行此操作,您需要控制可能發生此操作的上下文; 最簡單的方法是將其正常保持在信號屏蔽中,僅當jmp_buf
在阻塞調用周圍有效時才取消屏蔽它。 您調用的阻塞函數必須是異步信號安全的,並且不必是分配或釋放資源 (例如open
或close
)的函數,因為您在處理信號時將不知道它是否完成 。 當然, jmp_buf
或指向它的指針必須是線程局部對象( _Thread_local
/ __thread
),才能使其完全起作用。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.