0

I've been trying to learn how to multithread and came up with the following understanding. I was wondering if I'm correct or far off and, if I'm incorrect in any way, if someone could give me advice.

To create a thread, first you need to utilize a library such as <thread> or any alternative (I'm using boost's multithreading library to get cross-platform capabilities). Afterwards, you can create a thread by declaring it as such (for std::thread)

std::thread thread (foo); 

Now, you can use thread.join() or thread.detach(). The former will wait until the thread finishes, and then continue; while, the latter will run the thread alongside whatever you plan to do.

If you want to protect something, say a vector std::vector<double> data, from threads accessing simultaneously, you would use a mutex.

Mutex's would be declared as a global variable so that they may access the thread functions (OR, if you're making a class that will be multithreaded, the mutex can be declared as a private/public variable of the class). Afterwards, you can lock and unlock a thread using a mutex.

Let's take a quick look at this example pseudo code:

std::mutex mtx; std::vector<double> data; void threadFunction(){ // Do stuff // ... // Want to access a global variable mtx.lock(); data.push_back(3.23); mtx.unlock(); // Continue } 

In this code, when the mutex locks down on the thread, it only locks the lines of code between it and mtx.unlock(). Thus, other threads will still continue on their merry way until they try accessing data (Note, we would likely through a mutex in the other threads as well). Then they would stop, wait to use data, lock it, push_back, unlock it and continue. Check here for a good description of mutex's.

That's about it on my understanding of multithreading. So, am I horribly wrong or accurate?

9
  • 2
    Probably too broad a question for SO. "So, am I horribly wrong or accurate?" I'd say neither ;) Commented Oct 16, 2013 at 21:20
  • 2
    Side remark: If you use a mutex, you should probably use one of the RAII locks if possible. Commented Oct 16, 2013 at 21:22
  • 1
    You don't want to manual-lock that mutex. Where at all possible, you want to lock it with RAII to ensure any exceptions thrown don't leave any future waiters hanging in the wind with no place to go but back to the scheduler for another shot at waiting on something they can never get. Commented Oct 16, 2013 at 21:29
  • Note that what you call «protection» can be achieved by synchronisation primitives, like spin locks, mutexes and semaphores, but also by using lock free algorithms and structures, using atomic operations, depending on the platform. Commented Oct 16, 2013 at 21:30
  • Ya, that's about the gist of mutexes. What's your question? Commented Oct 16, 2013 at 21:32

1 Answer 1

1

Your comments refer to "locking the whole thread". You can't lock part of a thread.

When you lock a mutex, the current thread takes ownership of the mutex. Conceptually, you can think of it as the thread places its mark on the mutex (stores its threadid in the mutex data structure). If any other thread comes along and attempts to acquire the same mutex instance, it sees that the mutex is already "claimed" by somebody else and it waits until the first thread has released the mutex. When the owning thread later releases the mutex, one of the threads that is waiting for the mutex can wake up, acquire the mutex for themselves, and carry on.

In your code example, there is a potential risk that the mutex might not be released once it is acquired. If the call to data.push_back(xxx) throws an exception (out of memory?), then execution will never reach mtx.unlock() and the mutex will remain locked forever. All subsequent threads that attempt to acquire that mutex will drop into a permanent wait state. They'll never wake up because the thread that owns the mutex is toast.

For this reason, acquiring and releasing critical resources like mutexes should be done in a manner that will guarantee they will be released regardless of how execution leaves the current scope. In other languages, this would mean putting the mtx.unlock() in the finally section of a try..finally block:

mtx.lock(); try { // do stuff } finally { mtx.unlock(); } 

C++ doesn't have try..finally statements. Instead, C++ leverages its language rules for automatic disposal of locally defined variables. You construct an object in a local variable, the object acquires a mutex lock in its constructor. When execution leaves the current function scope, C++ will make sure that the object is disposed, and the object releases the lock when it is disposed. That's the RAII others have mentioned. RAII just makes use of the existing implicit try..finally block that wraps every C++ function body.

Sign up to request clarification or add additional context in comments.

4 Comments

One more thing I want to clarify. When calling a mutex, you say it "locks the whole thread". So does it lock all threads and only run the one thread until it unlocks the mutex?
You said "locks the whole thread", not I. The mutex only blocks other threads that attempt to acquire the mutex, when they attempt to acquire the mutex. If you have 500 threads and one of them acquires the mutex and the other 499 don't come anywhere near the mutex, then none of the 499 threads are in any way affected by the one thread which holds the mutex. If one of the 499 then takes an interest in the mutex and attempts to acquire it, that thread will block (do nothing) until the first thread releases the mutex. Score: 1 has mutex, 1 is waiting, and 498 don't care.
Thank you so much. That clears up a lot of confusion
I think I figured it out and understand it better. If you'ld like to see it, I put the code on code review.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.