Powered by
/src$ make
  • Home
  • About
  • Directory
  • Contact
  • Home
  • About
  • Directory
  • Contact

C++ Multithreading - Advanced Guide

1/21/2018

 
To see me speak about these topics and go over the code, please watch this youtube video.

Introduction

In another blog post, we learned about what multithreading is and how to do multithreading in C++. Please refer to that blog post if you're a beginner.

In this blog post, we're going to be looking at advanced C++ multithreading examples. 

Starting Out - No Multithreading

So let's say that we have a particular function that we want to use a bunch of times. This function is called srcmakeMulti and can be seen in the following example. 
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
///////////////////////////////////////////////
// Copyright srcmake.com 2018.
#include <iostream>
using namespace std;

int num = 0;

void srcmakeMulti()
        {
        num += 1; 
        cout << "srcmake called srcmakeMulti " << num << " times." << endl;
        }

int main()
        {
        srcmakeMulti();
        srcmakeMulti();
        srcmakeMulti();
        return 0;
        }
///////////////////////////////////////////////    
In this particular example, we call the function three times.
We can compile and run this code with the following two lines, in your terminal. We'll be using this command to run all of the following code, as well.
g++ main.cpp -std=c++11 -pthread -o run  
./run

Use Multithreading, But Notice The Issue

That's great, but what if we have to call the function many, many times? We could wait for the functions to execute sequentially, but it would be better to take advantage of multithreading to execute the function as often as we can, to finish quicker. 
 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
///////////////////////////////////////////////
// Copyright srcmake.com 2018.
#include <iostream>
#include <thread>
using namespace std;

int num = 0;

void srcmakeMulti()
        {
        num += 1; 
        cout << "srcmake called srcmakeMulti " << num << " times." << endl;
        }

int main()
        {
        // Spin each function onto a new thread.
        thread t1(srcmakeMulti);
        thread t2(srcmakeMulti);
        thread t3(srcmakeMulti);
        
        // Wait for each thread to finish before continuing on.
        t1.join();
        t2.join();
        t3.join();
        
        return 0;
        }
///////////////////////////////////////////////    
Fantastic, our code will be much faster now (assuming that our CPU has extra threads available), but we have some issues...

The problem is that we're using shared resources (the 'num' variable and 'cout') between multiple threads, and that leads to errors during execution. If you run the above code a few times, it's obvious that what gets outputted to the console isn't correct, and you get a different answer every time. (If you had one phone and three people, they can't all use the phone at the same time, right?)

Mutexes - Shared Resources Between Multiple Threads

The way we handle this is by "locking" parts of our code while it's using a shared resource. We do this using mutexes. A mutex will not let another thread access the part of the locked code until the mutex finishes executing that piece of code and unlocks it. Notice the four lines of additional code in the following example, and notice that the output is what we expect.
 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
///////////////////////////////////////////////
// Copyright srcmake.com 2018.
#include <iostream>
#include <thread>
#include <mutex>
using namespace std;

mutex mu;
int num = 0;

void srcmakeMulti()
        {
        // Lock this piece of code since it uses shared resources.
        mu.lock();
        num += 1; 
        cout << "srcmake called srcmakeMulti " << num << " times." << endl;
        mu.unlock();
        }

int main()
        {
        // Spin each function onto a new thread.
        thread t1(srcmakeMulti);
        thread t2(srcmakeMulti);
        thread t3(srcmakeMulti);
        
        // Wait for each thread to finish before continuing on.
        t1.join();
        t2.join();
        t3.join();
        
        return 0;
        }
///////////////////////////////////////////////    

Better For Some Purposes: Async and Futures

If you simply need to spin some work off on separate thread, and do something else while waiting for the return result, then use async and futures. Basically, async does some work on another thread, and the future facilitates the async so that when the task is complete, you can get the return value in the part of your code that you need it.
 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
///////////////////////////////////////////////
// Copyright srcmake.com 2018.
#include <iostream>
#include <future>
using namespace std;

int srcmakeMulti()
        {
        int ret = 0;
        for(int i = 0; i < 100000; i++)
                {
                ret += 1;
                }
        return ret;
        }

int main()
        {
        int num = 0;
        cout << num << endl;
                
        auto myFuture = std::async(std::launch::async, srcmakeMulti);
        
        if(myFuture.valid())
                {
                num = myFuture.get();
                }
        
        cout << num << endl;
        
        return 0;
        }
///////////////////////////////////////////////    
You can read up on the documentation for futures and async yourself, but for the most part, follow the syntax shown above. 

Final Pro-Tips

There are a few protips that we should adhere to to ensure that our code is good. These tips are heavily inspired by this article, so read the article after this.
  1. Make sure that if you use multiple locks, that you acquire them in the same order. Otherwise, you'll fall into a Deadlock. 
  2. Don't lock the same mutex twice, before unlocking it. 
​
Basically, be careful with your multithreading architecture, if your project is large.
To see me go over the code and speak about the topic, watch the following video:
Like this content and want more? Feel free to look around and find another blog post that interests you. You can also contact me through one of the various social media channels. 

Twitter: @srcmake
Discord: srcmake#3644
Youtube: srcmake
Twitch: www.twitch.tv/srcmake
​Github: srcmake
References:
1. stackoverflow.com/questions/4989451/mutex-example-tutorial
2. www.acodersjourney.com/2017/08/top-20-cplusplus-multithreading-mistakes/


Comments are closed.

    Author

    Hi, I'm srcmake. I play video games and develop software. 

    Pro-tip: Click the "DIRECTORY" button in the menu to find a list of blog posts.
    Metamask tip button
    License: All code and instructions are provided under the MIT License.

    Discord

    Chat with me.


    Youtube

    Watch my videos.


    Twitter

    Get the latest news.


    Twitch

    See the me code live.


    Github

    My latest projects.

Powered by Create your own unique website with customizable templates.