Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't these threads run in order?

When I run this code:

#include <iostream>
#include <thread>
#include <mutex>

std::mutex m;

int main()
{
    std::vector<std::thread> workers;
    for (int i = 0; i < 10; ++i)
    {
        workers.emplace_back([i]
        {
            std::lock_guard<std::mutex> lock(m);
            std::cout << "Hi from thread " << i << std::endl;
        });
    }

    std::for_each(workers.begin(), workers.end(), [] (std::thread& t)
    { t.join(); });
}

I get the output:

Hi from thread 7
Hi from thread 1
Hi from thread 4
Hi from thread 6
Hi from thread 0
Hi from thread 5
Hi from thread 2
Hi from thread 3
Hi from thread 9
Hi from thread 8

Even though I used a mutex to keep only one thread access at a time. Why isn't the output in order?

like image 544
template boy Avatar asked Dec 03 '14 21:12

template boy


4 Answers

What your mutex achieves is that no two threads print at the same time. However, they are still racing for which thread acquires the mutex first.

If you want to have serial execution you can just avoid threads at all.

like image 174
Stephan Dollberg Avatar answered Oct 10 '22 18:10

Stephan Dollberg


It is totally up to the operating system in which order threads are scheduled. You can not rely on any order. Assume it is totally random.

like image 28
Silicomancer Avatar answered Oct 10 '22 18:10

Silicomancer


You are passing a lambda to emplace_back which is used as a parameter for the std::thread constructor. See the details for emplace_back copy/pasted below from cppreference.com.

"Appends a new element to the end of the container. The element is constructed through std::allocator_traits::construct, which typically uses placement-new to construct the element in-place at the location provided by the container. The arguments args... are forwarded to the constructor as std::forward(args)...."

http://en.cppreference.com/w/cpp/container/vector/emplace_back

The mutex in the body of the lambda has no affect until the std::thread object has been fully constructed and running the thread entry point defined by the lambda body. Some of the std::threads could start running during the loop, or the threads might not start until after the loop completes. You can't figure this out in a portable way.

After the for-loop constructs all your std::threads in your vector, it is up to the OS to decide in what order the threads are scheduled to run and that is how you get the random ordering of your output.

like image 35
rparolin Avatar answered Oct 10 '22 19:10

rparolin


The execution order is immaterial, you cannot predict it and it will change from run to run and over time in a given run.

Your designs should never depend on thread execution order in any way or form.

It goes further than that: if N threads are waiting on a synchronization object, say a mutex, and the mutex gets released, there is no way to reliably and portably predict which of the waiting threads will be awaken and grab the mutex next, irrespectively of their relative scheduling priorities.

Non-determinism makes multithreaded programming difficult :-)

like image 27
Axel Rietschin Avatar answered Oct 10 '22 19:10

Axel Rietschin