C++ threads

Why is the operator's message printed and then the function's while they've been defined and joined the other way around in the code below?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
#include <thread>

struct F {
    void operator()() { std::cout << "Hello Parallel World!\n"; }
};

void f() {
    std::cout << "Hello World!\n";
}

int main()
{
    std::thread t1{ f };
    std::thread t2{ F()};

    t1.join();
    t2.join();

    system("pause");
    return 0;
}
Last edited on
The order that join is called isn't too important in this case. Joining is simply a blocking call that makes sure the thread completes before moving onward. It could be both threads had already completed their work before join was called for either. Your two threads are asynchronous and have no common thread. I suppose chances are f will happen before F but this should not be relied on.
Last edited on
The two threads are independent of each other - hence no assumptions should be made re the order these are executed - indeed for multi-core cpus they could be executed in parallel on different cores. Also note that output streams are not thread safe and if the same stream is used in multiple threads (like here for std:cout) then these operations should be synchronised.
By default, standard streams are thread safe.

Unless sync_with_stdio(false) has been issued, it is safe to concurrently access these objects from multiple threads for both formatted and unformatted output. https://en.cppreference.com/w/cpp/io/cout
Last edited on
it is safe to concurrently access these objects from multiple threads for both formatted and unformatted output.


Hmmm. I've certainly had issues with using std::cout in different threads where output from the threads have been 'intermixed' without using a sync lock. If we have to use a stream in a thread, we put it in a MS 'critical section' (wed don't use C++ threads).
@seeplus

Is it possible you used chained statements like
std::cout << "The answer is " << i <<".";

Even though it looks like a single print statement, this would be 3 separate insertion operations. Even if each stream operation is thread safe, there would be no such guarantee for the 3 of them together.

At least that's my speculation.
> If we have to use a stream in a thread, we put it in a MS 'critical section' (wed don't use C++ threads).

To make sure that the output would not be 'intermixed' (interleaved), use a synchronised output stream wrapper eg. std::osyncstream (defined in <syncstream>)

It provides the guarantee that all output made to the same final destination buffer (std::cout in the examples above) will be free of data races and will not be interleaved or garbled in any way, as long as every write to the that final destination buffer is made through (possibly different) instances of std::basic_osyncstream.
https://en.cppreference.com/w/cpp/io/basic_osyncstream
> Even though it looks like a single print statement, this would be 3 separate insertion operations.
> Even if each stream operation is thread safe, there would be no such guarantee for the 3 of them together.

Yes. Even though it looks like a single print statement, it is not a single stream operation.
To make sure that the output would not be 'intermixed' (interleaved), use a synchronised output stream wrapper eg. std::osyncstream (defined in <syncstream>)

From C++20.
In this newer version of the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <iostream>
#include <thread>
#include <vector>

struct F {
    std::vector<double>& v;
    F(std::vector<double>& vv) : v(vv) {}
    void operator()() {
        for (auto& d : v)
            d++;
    };
};

void f(std::vector<double>& v) {
    for (auto& d : v)
        d++;
}

int main()
{
    std::vector<double> some_vec{ 1,2,3,4,5,6,7,8,9 };
    std::vector<double> vec2{ 10, 11,12,13,14 };

    std::thread t1{ f, ref(some_vec)};
    std::thread t2{ F(vec2)};

    t1.join();
    t2.join();

    for (const auto& d : some_vec)
        std::cout << d << ' ';
    std::cout << '\n';

    for (const auto& d : vec2)
        std::cout << d << ' ';
    std::cout << '\n';

    system("pause");
    return 0;
}


Why do we need to define thread t1 with reference, ref(some_vec)?
And how to define it without reference, like a copy of the object?
Last edited on
> And how to define it without reference, like a copy of the object?

By default, the constructor of the thread object copies (or moves) the arguments.


> Why do we need to define thread t1 with reference, ref(some_vec)?

We do it when we want the thread to get a reference to the original object (rather than a copy).

The arguments to the thread function are moved or copied by value. If a reference argument needs to be passed to the thread function, it has to be wrapped (e.g., with std::ref or std::cref).
https://en.cppreference.com/w/cpp/thread/thread/thread#Notes



std::reference_wrapper:
It is frequently used as a mechanism to store references inside standard containers (like std::vector) which cannot normally hold references.
...
Helper functions std::ref and std::cref are often used to generate std::reference_wrapper objects.
...
std::reference_wrapper is also used to pass objects by reference to std::bind, the constructor of std::thread, or the helper functions std::make_pair and std::make_tuple.
https://en.cppreference.com/w/cpp/utility/functional/reference_wrapper
Thank you for your answer, but how to define a thread to get a copy of the object to be sent to a function? When I define t1 the following way, I get errors!
std::thread t1{ f, some_vec};

Severity Code Description Project File Line Suppression State
Error C2672 'invoke': no matching overloaded function found test_1 C:\Program Files\Microsoft Visual Studio\2022\Community\VC\Tools\MSVC\14.30.30705\include\thread 55


As well as, std::thread t1{ f(some_vec)}; doesn't work.
Pass the object by value.

1
2
3
4
5
void f( std::vector<double> v ) { // the function gets a copy of the vector

    for (auto& d : v)
        d++;
}

Topic archived. No new replies allowed.