C++ basic multithreading

Hi all,

In this following piece of 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
#include <iostream>
#include <thread>

static bool finished = false;

void doWork(int n) {

    using namespace std::literals::chrono_literals;

    std::cout << " Worker thread id: " << std::this_thread::get_id() <<'\n';

    while (!finished) {
        std::cout << " Working ..." << n << '\n';
        std::this_thread::sleep_for(1s);
    }
}

int main()
{
    std::thread worker(doWork, 8); // "doWork" is a pointer to the fucntion "doWork()"

    std::cin.get();
    finished = true;

    worker.join();

    std::cout << " main thread id: " << std::this_thread::get_id() << '\n';

    std::cin.get();
    return 0;
}


Is my understanding right?
The program starts from the main thread which is actually the main() function, then the second thread, worker, starts running and doing what for which is defined, while the main thread, too, is running at the same time, waiting for a character to get via std::cin.get(). Now we have two/multi threads working in parallel.
When we give std::cin(), a character, then the main thread proceeds to setting the bool variable, finished, to true. At that point, the second thread, which has been running alongside the main thread, stops, exiting the loop and finishing its task.
The worker.jopin() makes sure that the main thread can't go further this line until the worker thread has finished its job. By having the second/worker exit, the main thread is allowed to step further to std::cout... and then the rest of the code until return.

Last edited on
> Is my understanding right?

Yes.

Need to make the variable finished atomic.
"If one thread writes to an atomic object while another thread reads from it, the behaviour is well-defined"
https://en.cppreference.com/w/cpp/atomic/atomic

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
#include <iostream>
#include <thread>
#include <atomic> // ***
#include <chrono> // ***

static std::atomic<bool> finished = false;

void doWork(int n) {

    using namespace std::literals::chrono_literals;

    std::cout << " Worker thread id: " << std::this_thread::get_id() <<'\n';

    while (!finished) {
        std::cout << " Working ..." << n << '\n' << std::flush ;
        std::this_thread::sleep_for(1s);
    }
}

int main()
{
    std::thread worker(doWork, 8); // "doWork" is a pointer to the fucntion "doWork()"

    std::cin.get();
    finished = true;

    worker.join();

    std::cout << " main thread id: " << std::this_thread::get_id() << '\n';

    std::cin.get();
    return 0;
}

@JLBorges,
your code doesn't run on the shell.
6:37: error: use of deleted function 'std::atomic<bool>::atomic(const std::atomic<bool>&)'
In file included from 3:0:
/usr/include/c++/4.9/atomic:491:7: note: declared here
       atomic(const atomic&) = delete;
       ^
> your code doesn't run on the shell.

Make it:
1
2
// static std::atomic<bool> finished = false;
static std::atomic<bool> finished{ false }; 
Need to make the variable finished atomic.
Couple of questions:

a) why is #include <chrono> // *** needed while you have used using ... in doWork(), please?
b) why do we need static for that global variable, please?
c) How to learn C++ atomic? I read one of the books of Stroustrup on that but the topic is too complicated/sophisticated for me. Is there any reference to illustrate/explain is in a simple language, please?
Last edited on
> a) why is #include <chrono> // *** needed while you have used using ... in doWork()

These literals are defined (in an inline namespace) in <chrono>. In general, it is not a good (portable) idea to rely on a standard library header being implicitly included by another standard library header.


> b) why do we need static for that global variable, please?

We don't absolutely need it to be static.
Though, restricting visibility across translation units (forcing internal linkage) is a good habit to get into.


> c) How to learn C++ atomic?

Start by treating atomic variables like ordinary variables which are inherently thread-safe.
(the default for all atomic operations in the library is the super-safe sequentially consistent ordering)
https://en.cppreference.com/w/cpp/atomic/memory_order

At a later stage, you may want to explore fine-tuning performance in depth; this is good reference:
'C++ Concurrency in Action: Practical Multithreading' by Anthony Williams
https://www.amazon.com/C-Concurrency-Action-Practical-Multithreading/dp/1933988770
I didn't get a's answer! :(

restricting visibility across translation units (forcing internal linkage) is a good habit to get into.
Do we need to consider that mostly for global variables?

For atomic variables (and more generally C++ concurrency) I need something "concise and explained simply" to get across that since it's new for me.
static is useful in functions as well, to retain a value between calls:
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
#include <iostream>

int foo1()
{
   int x { };

   ++x;

   return x;
}

int foo2()
{
   static int y { };

   ++y;

   return y;
}

int main()
{
   for (int i { }; i < 5; ++i)
   {
      std::cout << foo1() << ' ' << foo2() << '\n';
   }
}
1 1
1 2
1 3
1 4
1 5
> Do we need to consider that mostly for global variables?

For names declared at namespace scope.
Internal linkage: The name can be referred to from all scopes in the current translation unit, (but not from scopes in other translation units.)
@George P: Thanks, good point! :)

Static elements are storage allocated only once in the static storage area, not stack or heap and hold that storage throughout the whole program lifetime. Do translation units (C++ files) use stack and heap storage areas (apart from their static variables/functions)?
> Do translation units use storage areas (apart from their static variables/functions)?

Yes.
Various features of the language, such as references and virtual functions, might involve additional memory locations that are not accessible to programs but are managed by the implementation.
https://en.cppreference.com/w/cpp/language/memory_model#Memory_Location


re. storage duration: https://cplusplus.com/forum/beginner/281173/2/#msg1216230
Topic archived. No new replies allowed.