- Boost.Interprocess允許多個進程同時使用共享內存。因為共享內存從定義上來說是進程間共享的,所以Boost.Interprocess需要支持某種同步。
- 想到同步,我們會想到C++11標準庫中的類或Boost.Thread。但是這些類只能用來同步同一進程內的線程,它們不支持不同進程的同步。不過,由于兩種情況下面臨的挑戰是一樣的,所以概念也是一樣的。
- 在多線程應用程序中,同步對象(如mutexes和條件變量)駐留在同一個地址空間中,因此所有線程都可以使用,而共享內存的挑戰是,獨立的進程需要共享這些對象。例如,如果一個進程創建了一個mutex,那么它就需要以某種方式從另一個進程中訪問。
- Boost.Interprocess提供了兩種同步對象:匿名對象直接存儲在共享內存中,這使得它們對所有進程自動可用。命名對象由操作系統管理,不存儲在共享內存中,可以通過名稱從程序中引用(named_mutex)。
Example 33.12. Using a named mutex with boost::interprocess::named_mutex
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <iostream>using namespace boost::interprocess;int main()
{managed_shared_memory managed_shm{open_or_create, "shm", 1024};int *i = managed_shm.find_or_construct<int>("Integer")();named_mutex named_mtx{open_or_create, "mtx"};named_mtx.lock();++(*i);std::cout << *i << '\n';named_mtx.unlock();
}
- Example?33.12?creates and uses a named mutex using the class?
boost::interprocess::named_mutex
, which is defined in?boost/interprocess/sync/named_mutex.hpp
. - boost::interprocess::named_mutex的構造函數期望得到一個參數,指定是否應該創建或打開mutex,以及mutex的名稱。每個知道這個名字的進程都可以打開同一個mutex。要訪問共享內存中的數據,程序需要通過調用成員函數lock()來獲得mutex的所有權。由于mutexes一次只能由一個進程擁有,所以另一個進程可能需要等待,直到mutex被unlock()釋放。一旦一個進程擁有了一個mutex的所有權,它就擁有了對mutex所保護的資源的獨占性訪問權。在示例33.12中,資源是一個類型為int的變量,它被遞增并寫入標準輸出流。
- 如果示例程序被多次啟動,每個實例都會打印一個比前一個值遞增1的值。由于有了mutex,不同進程之間對共享內存和變量本身的訪問是同步的。
Example?33.13.?Using an anonymous mutex with?interprocess_mutex
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <iostream>using namespace boost::interprocess;int main()
{managed_shared_memory managed_shm{open_or_create, "shm", 1024};int *i = managed_shm.find_or_construct<int>("Integer")();interprocess_mutex *mtx =managed_shm.find_or_construct<interprocess_mutex>("mtx")();mtx->lock();++(*i);std::cout << *i << '\n';mtx->unlock();
}
- Example?33.13?uses an anonymous mutex of type?
boost::interprocess::interprocess_mutex
, which is defined in?boost/interprocess/sync/interprocess_mutex.hpp
. In order for the mutex to be accessible for all processes, it is stored in the shared memory.? -
例33.13的行為與前一個完全相同。唯一不同的是mutex,它現在直接存儲在共享內存中。這可以通過類 boost::interprocess::managed_shared_memory 中的成員函數 construct() 或 find_or_construct() 來實現。除了lock(),boost::interprocess::named_mutex和boost::interprocess::interprocess_mutex都提供了成員函數try_lock()和timed_lock()。它們的行為與標準庫和Boost.Thread中的對應函數完全相同。如果需要遞歸互斥,Boost.Interprocess提供了兩個類:boost::interprocess::named_recursive_mutex和boost::interprocess::interprocess_recursive_mutex。
-
互斥保證了對共享資源的獨占訪問,而條件變量則控制了誰在什么時候擁有獨占訪問權。一般來說,Boost.Interprocess提供的條件變量與C++11標準庫和Boost.Thread提供的條件變量的工作原理類似。它們具有類似的接口,這使得這些庫的用戶在Boost.Interprocess中使用這些變量時,會立即感到賓至如歸。
Example?33.14.?Using a named condition with?boost::interprocess::named_condition
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/sync/named_condition.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <iostream>using namespace boost::interprocess;int main()
{managed_shared_memory managed_shm{open_or_create, "shm", 1024};int *i = managed_shm.find_or_construct<int>("Integer")(0);named_mutex named_mtx{open_or_create, "mtx"};named_condition named_cnd{open_or_create, "cnd"};scoped_lock<named_mutex> lock{named_mtx};while (*i < 10){if (*i % 2 == 0){++(*i);named_cnd.notify_all();named_cnd.wait(lock);}else{std::cout << *i << std::endl;++(*i);named_cnd.notify_all();named_cnd.wait(lock);}}named_cnd.notify_all();shared_memory_object::remove("shm");named_mutex::remove("mtx");named_condition::remove("cnd");
}
- Example?33.14?uses a condition variable of type?
boost::interprocess::named_condition
, which is defined in?boost/interprocess/sync/named_condition.hpp
. Because it is a named variable, it does not need to be stored in shared memory.? - 例33.14使用了一個類型為boost::interprocess::named_condition的條件變量,它定義在boost/interprocess/sync/named_condition.hpp中。因為它是一個命名變量,所以不需要存儲在共享內存中。
- 應用程序使用一個 while 循環來遞增一個類型為 int 的變量,該變量存儲在共享內存中。雖然變量隨著循環的每次迭代而遞增,但它只會在每第二次迭代時被寫入標準輸出流--只寫奇數。每當變量遞增1時,條件變量命名為_cnd的成員函數wait()被調用。一個鎖--在例33.14中,名為鎖的變量--被傳遞給這個成員函數。這是基于RAII的習慣,即在構造函數中取得一個mutex的所有權,并在destructor中釋放它。
- 這個鎖是在while循環之前創建的,并且在整個程序執行過程中擁有mutex的所有權。然而,如果將其作為參數傳遞給wait(),鎖就會自動釋放。條件變量用于等待指示等待結束的信號。同步是由成員函數wait()和notify_all()控制的。當程序調用wait()時,相應的mutex的所有權被釋放。然后,程序會一直等待,直到對同一個條件變量調用notify_all()。啟動時,例33.14似乎并沒有什么作用。在while循環內將變量i從0增到1后,程序通過調用wait()等待信號。為了發射信號,需要啟動第二個程序實例。
- 第二個實例在進入 while 循環之前,試圖取得同一個 mutex 的所有權。由于第一個實例通過調用wait()釋放了mutex,所以成功了。因為變量已經被遞增了一次,第二個實例執行if表達式的else分支,并將當前值寫入標準輸出流。然后將值遞增1。現在第二個實例也調用wait()。然而,在這之前,它調用notify_all(),這確保了兩個實例的正確合作。第一個實例得到通知,并試圖再次獲得mutex的所有權,而這個mutex仍然由第二個實例擁有。然而,由于第二個實例在調用notify_all()后立即調用wait(),它會自動釋放所有權,所以第一個實例將在此時獲得所有權。兩個實例交替進行,遞增共享內存中的變量。但是,只有一個實例將值寫入標準輸出流。當變量達到值10時,while循環就結束了。為了避免讓另一個實例永遠等待信號,循環結束后再調用一次notify_all()。在終止之前,共享內存、mutex和條件變量被銷毀。
- 就像有兩種類型的mutexes--一種是必須存儲在共享內存中的匿名類型,另一種是命名類型--條件變量也有兩種類型。例33.15是使用匿名條件變量重寫的上一個例子。
Example?33.15.?Using an anonymous condition with?interprocess_condition
#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/sync/interprocess_mutex.hpp>
#include <boost/interprocess/sync/interprocess_condition.hpp>
#include <boost/interprocess/sync/scoped_lock.hpp>
#include <iostream>using namespace boost::interprocess;int main()
{managed_shared_memory managed_shm{open_or_create, "shm", 1024};int *i = managed_shm.find_or_construct<int>("Integer")(0);interprocess_mutex *mtx =managed_shm.find_or_construct<interprocess_mutex>("mtx")();interprocess_condition *cnd =managed_shm.find_or_construct<interprocess_condition>("cnd")();scoped_lock<interprocess_mutex> lock{*mtx};while (*i < 10){if (*i % 2 == 0){++(*i);cnd->notify_all();cnd->wait(lock);}else{std::cout << *i << std::endl;++(*i);cnd->notify_all();cnd->wait(lock);}}cnd->notify_all();shared_memory_object::remove("shm");
}
- 例33.15的工作原理和上一個完全一樣,同樣需要啟動兩次才能將int變量遞增十次。除了mutexes和條件變量,Boost.Interprocess還支持semaphores和文件鎖。Semaphores與條件變量類似,只是它們不區分兩種狀態,而是基于一個計數器。文件鎖的行為類似于mutexes,只是它們用于硬盤上的文件,而不是內存中的對象。
- 就像C++11標準庫和Boost.Thread區分不同類型的mutexes和鎖一樣,Boost.Interprocess提供了幾種mutexes和鎖。例如,mutexes可以是獨占的,也可以是非獨占的。如果多個進程需要同時讀取數據,這很有幫助,因為只需要一個獨占的互斥來寫入數據。不同的鎖類可以將RAII習語應用于單個mutexes。
- 除非使用匿名同步對象,否則名稱應該是唯一的。即使mutexes和條件變量是基于不同類的對象,但對于由Boost.Interprocess封裝的操作系統依賴性接口來說,這不一定成立。在Windows上,mutexes和條件變量都使用相同的操作系統函數。如果兩個對象使用相同的名稱,每種類型的對象都有一個,那么程序在Windows上將無法正常運行。?
參考鏈接
- Synchronization