Linux下關於互斥鎖及同步的移植(二)
【原文:http://jazka.blog.51cto.com/809003/234734】
繼續上一篇,這篇來對比事件在win32和Linux下的區別。
在 Windows 中,事件物件是那些需要使用
SetEvent()
函式顯式地將其狀態設定為有訊號狀態的同步物件。事件物件來源有兩種型別:
- 在 手工重置事件(manual reset event) 中,物件的狀態會一直維持為有訊號狀態,直到使用
ResetEvent()
函式顯式地重新設定它為止。 - 在 自動重置事件(auto reset event) 中,物件的狀態會一直維持為有訊號狀態,直到單個正在等待的執行緒被釋放為止。當正在等待的執行緒被釋放時,其狀態就被設定為無訊號的狀態。
事件物件有兩種狀態,有訊號(signaled)狀態 和 無訊號(non-signaled)狀態。對事件物件呼叫的等待函式會阻塞呼叫執行緒,直到其狀態被設定為有訊號狀態為止。
在進行平臺的遷移時,需要考慮以下問題:
- Windows 提供了 有名(named) 和 無名(un-named) 的事件物件。有名事件物件用來在程式之間進行同步,而在 Linux 中, pthreads 和 POSIX 都提供了執行緒間的同步功能。為了在 Linux 實現與 Windows 中有名事件物件相同的功能,可以使用 System V 訊號量或訊號。
- Windows 提供了兩種型別的事件物件 —— 手工重置物件和自動重置物件。Linux 只提供了自動重置事件的特性。
- 在 Windows 中,事件物件的初始狀態被設定為有訊號狀態。在 Linux 中,pthreads 並沒有提供初始狀態,而 POSIX 訊號量則提供了一個初始狀態。
- Windows 事件物件是非同步的。在 Linux 中,POSIX 訊號量和 System V 訊號量也都是非同步的,不過 pthreads 條件變數不是非同步的。
- 當在一個等待函式中使用事件物件時,可以指定 Windows 的事件物件的超時時間值。在 Linux 中,只有 pthreads 在等待函式中提供了超時的特性。
還有幾點非常重要,需要說明一下:
- 儘管 POSIX 訊號量是計數器訊號量,但是當這個計數器被設定為 1 時,它們可以提供與 Windows 事件物件相似的功能。它們並不能在等待函式中提供超時時間。如果在進行移植時,超時並不是一個影響因素,那麼建議您使用 POSIX 訊號量。
- 當與互斥一起使用時,pthreads 條件變數可以線上程之間提供基於事件的同步機制,不過這是同步的。根據應用程式的邏輯,這可以將此作為移植過程中在 Linux 上實現這種功能的一個選擇。
在 Windows 中,我們使用
CreateEvent()
來建立事件物件。
HANDLE CreateEvent( LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCTSTR lpName ) |
在這段程式碼中:
lpEventAttributes
是一個指標,它指向一個決定這個控制程式碼是否能夠被繼承的屬性。如果這個指標為 NULL,那麼這個物件就不能被初始化。bManualReset
是一個標記,如果該值為 TRUE,就會建立一個手工重置的事件,應該顯式地呼叫ResetEvent()
,將事件物件的狀態設定為無訊號狀態。bInitialState
是這個事件物件的初始狀態。如果該值為 true,那麼這個事件物件的初始狀態就被設定為有訊號狀態。lpName
是指向這個事件物件名的指標。對於無名的事件物件來說,該值是 NULL。
這個函式建立一個手工重置或自動重置的事件物件,同時還要設定改物件的初始狀態。這個函式返回事件物件的控制程式碼,這樣就可以在後續的呼叫中使用這個事件物件了。
OpenEvent()
用來開啟一個現有的有名事件物件。這個函式返回該事件物件的控制程式碼。
HANDLE OpenEvent( DWORD dwDesiredAccess, BOOL bInheritHandle, LPCTSTR lpName ) |
在這段程式碼中:
dwDesiredAccess
是針對這個事件物件所請求的訪問權。bInheritHandle
是用來控制這個事件物件控制程式碼是否可繼承的標記。如果該值為 TRUE,那麼這個控制程式碼就可以被繼承;否則就不能被繼承。lpName
是一個指向事件物件名的指標。
在 Linux 中,可以呼叫
sem_init()
來建立一個 POSIX 訊號量:int sem_init(sem_t *sem, int pshared, unsigned int value)
(其中
value
(即訊號量計數值)被設定為這個訊號量的初始狀態)。
Linux pthreads 使用
pthread_cond_init()
來建立一個條件變數:int pthread_cond_init(pthread_cond_t *cond, pthread_condattr_t *cond_attr)
。
可以使用
PTHREAD_COND_INITIALIZER
常量靜態地對
pthread_cond_t
型別的條件變數進行初始化,也可以使用
pthread_condattr_init()
對其進行初始化,這個函式會對與這個條件變數關聯在一起的屬性進行初始化。可以呼叫
pthread_condattr_destroy()
用來銷燬屬性:
int pthread_condattr_init(pthread_condattr_t *attr) int pthread_condattr_destroy(pthread_condattr_t *attr) |
等待某個事件
在 Windows 中,等待函式提供了獲取同步物件的機制。我們可以使用不同型別的等待函式(此處我們只考慮
WaitForSingleObject()
)。這個函式會使用一個互斥物件的控制程式碼,並一直等待,直到它變為有訊號狀態或超時為止。
DWORD WaitForSingleObject( HANDLE hHandle, DWORD dwMilliseconds ); |
在這段程式碼中:
hHandle
是指向互斥控制程式碼的指標。dwMilliseconds
是超時時間的值,單位是毫秒。如果該值為INFINITE
,那麼它阻塞呼叫執行緒/程式的時間就是不確定的。
Linux POSIX 訊號量使用
sem_wait()
來掛起呼叫執行緒,直到訊號量的計數器變成非零的值為止。然後它會自動減小訊號量計數器的值:int sem_wait(sem_t * sem)
。
在 POSIX 訊號量中並沒有提供超時操作。這可以通過在一個迴圈中執行非阻塞的
sem_trywait()
來實現,該函式會對超時時間進行計數:int sem_trywait(sem_t * sem)
.
Linux pthreads 使用
pthread_cond_wait()
來阻塞呼叫執行緒,其時間是不確定的:int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex)
。在另外一方面,如果呼叫執行緒需要被阻塞一段確定的時間,那麼就可以使用
pthread_cond_timedwait()
來阻塞這個執行緒。如果在這段指定的時間內條件變數並沒有出現,那麼
pthread_cond_timedwait()
就會返回一個錯誤:int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex,const struct timespec *abstime)
。在這裡,abstime
引數指定了一個絕對時間(具體來說,就是從 1970 年 1 月 1 日 0 時 0 分 0 秒到現在所經過的時間。)函式
SetEvent()
用來將事件物件的狀態設定為有訊號狀態。對一個已經設定為有訊號狀態的事件物件再次執行該函式是無效的。
BOOL SetEvent( HANDLE hEvent ) |
Linux POSIX 訊號量使用
sem_post()
來發出一個事件訊號量。這會喚醒在該訊號量上阻塞的所有執行緒:int sem_post(sem_t * sem)
。
呼叫
pthread_cond_signal()
被用在 LinuxThreads 中,以喚醒在某個條件變數上等待的一個執行緒,而
pthread_cond_broadcast()
用來喚醒在某個條件變數上等待的所有執行緒。
int pthread_cond_signal(pthread_cond_t *cond) int pthread_cond_broadcast(pthread_cond_t *cond) |
注意,條件函式並不是非同步訊號安全的,因此不能在訊號處理函式中呼叫。具體地說,在訊號處理函式中呼叫
pthread_cond_signal()
或
pthread_cond_broadcast()
可能會導致呼叫執行緒的死鎖。在 Windows 中,
ResetEvent()
用來將事件物件的狀態重新設定為無訊號狀態。
BOOL ResetEvent( HANDLE hEvent ); |
在 Linux 中,條件變數和 POSIX 訊號量都是自動重置型別的。
在 Windows 中,
CloseHandle()
用來關閉或銷燬事件物件。
BOOL CloseHandle( HANDLE hObject ); |
在這段程式碼中,
hObject
是指向同步物件控制程式碼的指標。
在 Linux 中,
sem_destroy()/ pthread_cond_destroy()
用來銷燬訊號量物件或條件變數,並釋放它們所持有的資源:
int sem_destroy(sem_t *sem) int pthread_cond_destroy(pthread_cond_t *cond) |
在 Linux 中,程式之間有名事件物件所實現的功能可以使用 System V 訊號量實現。System V 訊號量是計數器變數,因此可以實現 Windows 中事件物件的功能,訊號量的計數器的初始值可以使用
semctl()
設定為 0。 要將某個事件的狀態修改為有訊號狀態,可以使用
semop()
,並將
sem_op
的值設定為 1。要等待某個事件,則可以使用
semop()
函式,並將 sem_op
的值設定為 -1,這樣就可以阻塞呼叫程式,直到它變為有訊號狀態為止。
可以通過使用
semctl()
將訊號量計數器的初始值設定為 0 來獲得訊號量。在使用完共享資源之後,可以使用
semop()
將訊號量計數設定為 1。關於每個 System V 訊號量的原型,請參閱本文中有關訊號量一節的內容。 相關文章
- Linux下關於互斥鎖及同步的移植(一)Linux
- Linux下關於互斥鎖及同步的移植(一)薦Linux
- 執行緒同步與互斥:互斥鎖執行緒
- 透過互斥鎖+條件量的方式實現同步與互斥
- Go 互斥鎖 Mutex 原始碼分析(二)GoMutex原始碼
- Java虛擬機器13:互斥同步、鎖優化及synchronized和volatileJava虛擬機優化synchronized
- 關於Linux下ntp 時間同步錯誤Linux
- Linux之執行緒互斥鎖Linux執行緒
- Onvif開發之Linux下gsoap的使用及移植Linux
- Python提高:關於GIL(全域性直譯器鎖)與執行緒互斥鎖的理解Python執行緒
- 關於WINDOWS平臺下RMAN備份移植Windows
- 關於同步的一點思考-下
- Linux多執行緒的使用一:互斥鎖Linux執行緒
- 【go】golang中鎖的用法-互斥鎖Golang
- 多執行緒(2)-執行緒同步互斥鎖Mutex執行緒Mutex
- Jtti:linux下訊號量和互斥鎖有哪些區別?JttiLinux
- linux多執行緒-----同步物件(互斥量、讀寫鎖、條件變數)的屬性Linux執行緒物件變數
- linux多執行緒-----同步機制(互斥量、讀寫鎖、條件變數)Linux執行緒變數
- Gil全域性解釋鎖和執行緒互斥鎖的關係執行緒
- Python中的互斥鎖Python
- 執行緒的互斥鎖執行緒
- liteos互斥鎖(七)
- Go併發程式設計之傳統同步—(1)互斥鎖Go程式設計
- 關於JAVA的可移植性(轉)Java
- linux程式多執行緒互斥鎖的簡單使用Linux執行緒
- C 語言的 互斥鎖、自旋鎖、原子操作
- Go語言的互斥鎖MutexGoMutex
- 關於Gdb工具的交叉編譯、移植編譯
- 【linux】系統程式設計-6-POSIX標準下的訊號量與互斥鎖Linux程式設計
- 一文看懂臨界區、互斥鎖、同步鎖、臨界區、訊號量、自旋鎖等名詞!
- 物聯網學習教程——執行緒同步與互斥:讀寫鎖執行緒
- TM表鎖各種mode的實驗及2-6 的TM鎖相互間的互斥示例
- 互斥鎖mutex的簡單實現Mutex
- 特定的閂鎖和互斥場景
- 淺談程式同步和互斥的概念
- 自旋鎖和互斥鎖區別 --- 經典
- 多工同步與互斥概念
- 讀寫鎖 ReentrantReadWriteLock 與 互斥鎖 的效率