Valid STL thread security features of STL containers

Source: Internet
Author: User

 

The world of Standard C ++ is quite conservative and streamlined. In this pure world, All executable files are statically linked. No memory ing file or shared memory exists. There is no window system, no network, no database, no process. In this case, you shouldn't be surprised when you find that the standard does not mention anything about the thread. The first idea of STL thread security depends on STL implementation.

 

Of course, multithreading programs are common, so most STL vendors try to make their implementations work normally in the thread environment. However, even if they do well, most of the burden is still on your shoulders, and understanding why this is important. STL vendors can only do things for you to reduce the pain of multithreading, and you need to know what they have done.

The golden rules supported by multithreading in STL containers (and the wishes of most vendors) have been defined by SGI and released on their STL website [21. In general, you can determine at most the following content from the implementation:

  • Multiple readers are secure.Multithreading may read the content of a container at the same time, which will be correctly executed. Of course, no writer can operate on this container during reading.
  • It is safe for multiple writers of different containers.Multiple Threads can write different containers at the same time.

That's all. Some implementations of STL provide these guarantees, but some cannot.

 

It is difficult to write multi-threaded code. Many programmers want STL implementation to be completely thread-safe. In that case, programmers no longer need to perform synchronization control on their own. There is no doubt that this will bring a lot of convenience to users, but it is not easy to implement. A library may try to implement such a fully thread-Safe Container in the following ways:

  • Lock the container during each call to the container's member function.
  • Lock the container within the lifetime of each iterator returned by the container (for example, by calling begin or end.
  • Lock the container during execution of each algorithm called on the container. (This is actually meaningless because, as explained in cla32, algorithms cannot identify the containers they are operating on. However, we will test this option here, because its educational significance is to see why it cannot work even if it is possible .)

Consider the following code. It searches for the first place where the value 5 appears in a vector <int>. if it finds the value, it changes it to 0.

Vector <int> V; vector <int>: iterator first5 (find (V. Begin (), V. End (), 5); // line 1if (first5! = V. End () {// Row 2 * first5 = 0; // Row 3}

In a multi-threaded environment, another thread may immediately modify the data in V after row 1 is completed. In that case, Row 2's first5 and V. End detection will be meaningless, because the value of V may be different from the value at the end of Row 1. In fact, such detection will produce undefined results, because another thread may be inserted between Row 1 and row 2, making first5 invalid, maybe a insert operation causes the vector to re-allocate its internal memory. (That will invalidate all the iterators of the vector. For details about reallocation, refer to Clause 14 .) Similarly, the * first5 value assignment in Row 3 is insecure, because another thread may execute between Row 2 and row 3 and invalidate first5 in some way, you may delete the elements that it points to (or at least once points.

 

The synchronization mentioned above cannot avoid these problems. The begin and end calls in Row 1 are quickly returned, so that they cannot provide any help. The iterators generated by them only last until the end of the line, and find also returns in that line.

To ensure the above Code is thread-safe, V must be locked from row 1 to row 3. It is hard to imagine how the STL implementation can automatically infer this. Remember that synchronization primitives (such as signal lights, mutex volumes, and so on) are usually costly, it is harder to imagine how to achieve what we mentioned above without significant performance loss in the program-design in such a way-to allow up to one thread to access V in 1-3 rows.

 

This explains why you cannot expect any STL implementation to make your thread grief disappear. Instead, you must manually handle synchronization control in these cases. In this example, you can do the following:

Vector <int> V ;... getmutexfor (V); vector <int>: iterator first5 (find (v. begin (), V. end (), 5); If (first5! = V. End () {// It is now secure * first5 = 0; // It is also} releasemutexfor (v );

A more object-oriented solution is to create a lock class, obtain mutex in its constructor, and release it in its destructor, in this way, the chance of non-Matching calls of getmutexfor and releasemutexfor is minimized. Such a class (actually a class template) is basically like this:

Template <typename container> // obtain and release the container's mutex class lock {// The template core of the class; public: // ignores a lot of details lock (const containers container ): C (container) {getmutexfor (c); // obtain the mutex In the constructor }~ Lock () {releasemutexfor (c); // release it in the Destructor} PRIVATE: const container & C ;};

The method of using a class (like Lock) to manage the resource lifetime (such as mutex) is usually calledInitialization upon resource acquisitionYou should be able to read it in any comprehensive C ++ teaching material. A good start is stroustrup's "The C ++ programming language", because stroustrup recommends this idiom, but you can also go to Article 9 of "more effective C ++. Regardless of the source, remember that the above lock is the most basic implementation. A version of industrial strength requires many improvements, but such expansion is irrelevant to STL. And this minimized lock is enough to see how we can use it for the example we have been considering:

Vector <int> V ;... {// create a new block; lock <vector <int> lock (V); // obtain the mutex vector <int>: iterator first5 (find (v. begin (), V. end (), 5); If (first5! = V. End () {* first5 = 0 ;}// close the block and automatically release the mutex

Because the Lock Object releases the mutex of the container in the lock destructor, it is very important to destroy the lock when the mutex needs to be released. To make this happen, we create a new block with lock defined in it, and close the block when we no longer need mutex. It sounds like we only need to close the new block in exchange for calling releasemutexfor, but this is a false comment. If we forget to create a new block for lock, the mutex will be released, but it may happen later than it should -- when the control reaches the end of the closed block. If we forget to call releasemutexfor, we will not release the mutex.

 

Moreover, this lock-based method is robust when exceptions occur. C ++ ensures that if an exception is thrown, the partial object will be destroyed. Even if an exception is thrown when the Lock Object is being used, the lock will release its mutex. If we rely on manually calling getmutexfor and releasemutexfor, we will not release the mutex if an exception is thrown before releasemutexfor is called.

 

Exceptions and resource management are important, but they are not the subject of these terms. These terms are about thread security in STL. When it comes to thread security and STL containers, you 'd better have any expectations for STL. You need to control all aspects of multi-thread control by yourself. Thread security has never been STL's expertise.

 

 

Many containers in Java are implemented as thread-safe because JAVA supports multithreading at the language level. C ++ does not. When you use multithreading in C ++, you only rely on the operating system. Many libraries help You encapsulate these, such as the boost thread library. We suggest you go and have a look.

 

 

 

 

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.