简体   繁体   中英

C++ Thread safe vector.erase

I wrote a threaded Renderer for SFML which takes pointers to drawable objects and stores them in a vector to be draw each frame. Starting out adding objects to the vector and removing objects to the vector would frequently cause Segmentation faults (SIGSEGV). To try and combat this, I would add objects that needed to be removed/added to a queue to be removed later (before drawing the frame). This seemed to fix it, but lately I have noticed that if I add many objects at one time (or add/remove them fast enough) I will get the same SIGSEGV.

Should I be using locks when I add/remove from the vector?

You need to understand the thread-safety guarantees the C++ standard (and implementations of C++2003 for possibly concurrent systems) give. The standard containers are a thread-safe in the following sense:

  1. It is OK to have multiple concurrent threads reading the same container.
  2. If there is one thread modifying a container there shall be no concurrent threads reading or writing the same container.
  3. Different containers are independent of each other.

Many people misunderstand thread-safety of container to mean that these rules are imposed by the container implementation: they are not! It is your responsibility to obey these rules.

The reason these aren't, and actually can't, be imposed by the containers is that they don't have an interface suitable for this. Consider for example the following trivial piece of code:

if (!c.empty() {
    auto value = c.back();
    // do something with the read value
}

The container can control the access to the calls to empty() and back() . However, between these calls it necessarily needs to release any sort of synchronization facilities, ie by the time the thread tries to read c.back() the container may be empty again! There are essentially two ways to deal with this problem:

  1. You need to use external locking if there is possibility that a concurrent thread may be changing the container to span the entire range of accesses which are interdependent in some form.
  2. You change the interface of the containers to become monitors . However, the container interface isn't at all suitable to be changed in this direction because monitors essentially only support "fire and forget" style of interfaces.

Both strategies have their advantages and the standard library containers are clearly supporting the first style, ie they require external locking when using concurrently with a potential of at least one thread modifying the container. They don't require any kind of locking (neither internal or external) if there is ever only one thread using them in the first place. This is actually the scenario they were designed for. The thread-safety guarantees given for them are in place to guarantee that there are no internal facilities used which are not thread-safe, say one per-object iterator object or a memory allocation facility shared by multiple threads without being thread-safe, etc.

To answer the original question: yes, you need to use external synchronization, eg in the form of mutex locks, if you modify the container in one thread and read it in another thread.

Should I be using locks when I add/remove from the vector?

Yes. If you're using the vector from two threads at the same time and you reallocate, then the backing allocation may be swapped out and freed behind the other thread's feet. The other thread would be reading/writing to freed memory, or memory in use for another unrelated allocation.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM