1. 程式人生 > >周海漢:標準模板庫stl 容器的執行緒安全

周海漢:標準模板庫stl 容器的執行緒安全

2007-12-05

周海漢/文

標準模板庫現在應用越來越廣泛。但它的容器是不是執行緒安全的呢?我們看到Windows平臺VC用的PJ STL,MSDN是這樣說的:

Thread Safety in the Standard C++ Library When /MT, /MTd, /MD, or /MDd is used, the following thread-safety rules are in effect: Container Classes and complex The container classes are vector, deque, list, queue, stack , priority_queue, valarray, map, multimap, set, multiset, basic_string, bitset.

For reads to the same object, the object is thread safe for reading in the following scenarios: From one thread at a time when no writers on other threads. From many threads at a time when no writers on other threads. For writes to the same object, the object is thread safe for writing from one thread when no readers on other threads
For reads to different objects of the same class, the object is thread safe for reading in the following scenarios: From one thread at a time. From one thread at a time when no writers on other threads. From many threads at a time. From many threads at a time when no writers on other threads.
For writes to different objects of the same class, the object is thread safe for writing in the following scenarios: From one thread when no readers on other threads. From many threads. For example, given objects A and B of the same class, it is safe if object A is being written in thread 1 and B is being read in thread 2. iostream Classes Note that reading from a stream buffer is not considered to be a read operation. It should be considered as a write operation, because this changes the state of the class. For reads to the same object, the object is thread safe for reading in the following scenarios: From one thread at a time when no writers on other threads. From many threads at a time when no writers on other threads. For writes to the same object, the object is thread safe for writing in the following scenarios: From one thread when no readers on other threads. From many threads (when accesses are limited to stream buffers). For reads to different objects of the same class, the object is thread safe for reading in the following scenarios: From one thread at a time. From one thread at a time when no writers on other threads. From many threads at a time. From many threads at a time when no writers on other threads. For writes to different objects of the same class, the object is thread safe for writing in the following scenarios: From one thread when no readers on other threads From many threads

意思就是:設定/MT, /MTd, /MD, or /MDd 標 志時,對vector, deque, list, queue, stack , priority_queue, valarray, map, multimap, set, multiset, basic_string, bitset 容器,多執行緒讀同一物件或同一個類的不同物件,沒有寫時,讀安全。可以多執行緒同時寫一個物件或一個類的不同物件,但不能有讀的執行緒。例如,一個執行緒寫一個 類物件A,另一執行緒寫該類的另一物件B,就是安全。 對iostream操作,從buffer讀操作也看成寫因為讀會更改stream狀態。其執行緒 安全和容器類似,也是可以同時讀,同時寫,但不能又讀又寫。如果會有併發讀寫的可能,對一個物件,必須加互斥。而如果是同一個類的不同物件,則不會影響。

那 麼SGL STL實現的多執行緒安全如何呢?

Thread-safety for SGI STL SGI STL provides what we believe to be the most useful form of thread-safety. This explains some of the design decisions made in the SGI STL implementation. Client must lock shared mutable containers The SGI implementation of STL is thread-safe only in the sense that simultaneous accesses to distinct containers are safe, and simultaneous read accesses to to shared containers are safe. If multiple threads access a single container, and at least one thread may potentially write, then the user is responsible for ensuring mutual exclusion between the threads during the container accesses. This is the only way to ensure full performance for containers that do not need concurrent access. Locking or other forms of synchronization are typically expensive and should be avoided when not necessary. It is easy for the client or another library to provide the necessary locking by wrapping the underlying container operations with a lock acquisition and release. For example, it would be possible to provide a locked_queue container adapter that provided a container with atomic queue operations. For most clients, it would be insufficient to simply make container operations atomic; larger grain atomic actions are needed. If a user’s code needs to increment the third element in a vector of counters, it would be insuffcient to guarantee that fetching the third element and storing the third element is atomic; it is also necessary to guarantee that no other updates occur in the middle. Thus it would be useless for vector operations to acquire the lock; the user code must provide for locking in any case. This decision is different from that made by the Java designers. There are two reasons for that. First, for security reasons Java must guarantee that even in the presence of unprotected concurrent accesses to a container, the integrity of the virtual machine cannot be violated. Such safety constraints were clearly not a driving force behind either C++ or STL. Secondly, performance was a more important design goal for STL than it was for the Java standard library. On the other hand, this notion of thread-safety is stronger than that provided by reference-counted string implementations that try to follow the CD2 version of the draft standard. Such implementations require locking between multiple readers of a shared string. Lock implementation The SGI STL implementation removes all nonconstant static data from container implementations. The only potentially shared static data resides in the allocator implementations. To this end, the code to implement per-class node allocation in HP STL was transformed into inlined code for per-size node allocation in the SGI STL allocators. Currently the only explicit locking is performed inside allocators. Many other container implementations should also benefit from this design. It will usually be possible to implement thread-safe containers in portable code that does not depend on any particular thread package or locking primitives. Alloc.h uses three different locking primitives depending on the environment. In addition, it can be forced to perform no locking by defining _NOTHREADS. The three styles of locking are: Pthread mutexes. These are used if _PTHREADS is defined by the user. This may be done on SGI machines, but is not recommended in performance critical code with the currently (March 1997) released versions of the SGI Pthreads libraries. Win32 critical sections. These are used by default for win32 compilations with compiler options that request multi-threaded code. An SGI specific spin-lock implementation that is usable with both pthread and sproc threads. This could serve as a prototype implementation for other platforms. This is the default on SGI/MIPS platforms. It would be preferable if we could always use the OS-supplied locking primitives. Unfortunately, these often do not perform well, for very short critical sections such as those used by the allocator. Allocation intensive applications using Pthreads to obtain concurrency on multiprocessors should consider using pthread_alloc from pthread_alloc.h. It imposes the restriction that memory deallocated by a thread can only be reallocated by that thread. However, it often obtains significant performance advantages as a result.

其大意也是,為了效率,沒有給所有操作加鎖。不同執行緒同時讀同一容器物件沒關係,不同執行緒同時寫不同的容器物件沒關係。但不能同時又讀又寫同一容器 物件的。因此,多執行緒要同時讀寫時,還是要自己加鎖。

STLPort基於SGI STL,只不過是支援跨平臺的,其實現和SGI STL沒有本質差別。

結 論:

STL 的實現,是部分執行緒安全的。就是說,對容器和iostream,如果不同執行緒同時讀同一容器物件執行緒安全。不同執行緒同時寫同一容器的不同物件,執行緒安全。 但不同執行緒同時讀寫同一物件,必須在外面自己做執行緒互斥和同步。

如非註明轉載, 均為原創. 本站遵循知識共享CC協議,轉載請註明來源