ontherocks
ontherocks

Reputation: 1999

Multi-threading - Do threads start running at the exact same time?

Trying to understand multi-threading. If I understand correctly, threads do not start at the same exact time, they just run in parallel starting one after the other in the sequence written. Let's take an example

#include <string>
#include <iostream>
#include <thread>

using namespace std;

void task(string msg, int index)
{
    cout << "Thread " << index << " says: " << msg << endl;    
}

int main()
{
    cout << "Main thread begins" << endl;

    thread t1(task, "Hello", 1);
    thread t2(task, "Hello", 2);
    thread t3(task, "Hello", 3);
    thread t4(task, "Hello", 4);

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    cout << "Back in main thread" << endl;

    return 0;
}

Since the program executes line by line, so thread t1 will start first, t2 second, t3 third and t4 fourth. Once started, they will run in parallel (along with the main thread). The above program will display incoherent strings because this is a too simple and quick operation for each thread racing to write to the console.

If we give each thread some time, then the output will be coherent. For example

#include <string>
#include <iostream>
#include <thread>

using namespace std;
using namespace std::this_thread;
using namespace std::chrono;

void task(string msg, int index)
{
    cout << "Thread " << index << " says: " << msg << endl;    
}

int main()
{
    cout << "Main thread begins" << endl;

    thread t1(task, "Hello", 1);
    sleep_for(seconds(3));
    thread t2(task, "Hello", 2);
    sleep_for(seconds(3));
    thread t3(task, "Hello", 3);
    sleep_for(seconds(3));
    thread t4(task, "Hello", 4);
    sleep_for(seconds(3));

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    cout << "Back in main thread" << endl;

    return 0;
}

Did not find stated explicitly anywhere about the starting time of threads. Is this understanding correct?

Upvotes: 2

Views: 1682

Answers (1)

Omnifarious
Omnifarious

Reputation: 56048

Counting on anything about when a thread starts or doesn't is generally a programming error. Yes, technically a thread is launched as soon as you create it. But that doesn't means it starts running immediately. The OS scheduler might do any number of things there.

Your attempt to coax the scheduler into doing what you want with sleep isn't even guaranteed to work, as you likely are aware.

If you need threads to synchronize their execution with respect to one another, you need to do something explicit to make that happen. Your sleep doesn't count, it's just a prayer in the hopes that things come out the way you want.

Synchronization primitives like ::std::mutex are popular. I've also used communication on a pipe, though that's more commonly used to synchronize between processes.

In Linux, for example, creating a ::std::thread results in a call to the the clone system call. This call acts directly opposite to the usual saying (two enter, but only one may leave) instead one enters and two leave. One thread makes the system call, and it's returned from twice, once in each thread. Which thread it returns in first is completely arbitrary. And if you have two cores, there may be no sensible answer to that question. A thread is executing as soon as that system call is returned from.

So, if you are creating multiple threads in a sequence of instructions executing in one thread, the kernel data structures describing these new threads are created in the order of execution that appears in your program. But that doesn't mean any of their instructions run. It may be that the instructions to create all four of them run in a continuous uninterrupted sequence, and then the kernel decides to suspend the original thread and execute the fourth one, but only 5 CPU instructions worth, and then the 1st, and then the 3rd. It might be that the kernel immediately schedules the first thread you create on a separate core so it's instructions execute literally at the same time as the kernel data structure for the next thread is being created.

All that's guaranteed here about execution order is that the original thread will make four sequential requests to create 4 new kernel data structures describing four new threads of execution. Those structures will be created in sequential order, but the order of their creation has absolutely nothing whatsoever to do with the order in which any of their instructions are executed.

Upvotes: 4

Related Questions