Effective Modern C++:multi-threading

Traditional Thread

C++ 11 supports multi-threading, for which we previously used pthread or boost. Standard C++ 11’s threading is based on boost::thread, now it’s cross platform and no dependency needed.

#include <thread>
#include <iostream>
int testThread()
{
	std::cout << "I am a seperate thread...";
}
void main()
{
	std::thread newThread(testThread);//now it's running
        //do something else .... //
	newThread.join();//before the ending, newThread should join
        return;
}

The code is simple, the thread function will run right after the std::thread is declared.

The newThread must join the main thread before the end of the program, “join” means the main thread will wait for the newThread to be finished.

The std::thread has a obvious drawback: it is simply a thread, it can not return any value. So if we want to get something from a thread, we can only use some global variable to exchange the result. Even we use global variable to pass result, we still don’t know when the result is ready, then we need one more variable as signal variable to record the status, that’s not acceptable.

Prefer std::async to std::thread

One solution C++ 11 provides is the task-based std::async, it’s even simpler:

#include <future>
#include <iostream>
#include <chrono>
int testThread()
{
	std::this_thread::sleep_for(std::chrono::seconds(3));
	std::cout << "slept for 3 second\n";
	return 1;
}
void main()
{
	auto result = std::async(std::launch::async, testThread);
        //do something else//
        return;
}

Just declare the std::async like the std::thread, done.

You don’t need to care anything else, the std::async will take care of everything, actually, it is managing the threads for you.

But there are a lot of tricks you need to know:

1 std::async must be assigned to a variable

This is different from std::thread, if we only declare it like:

std::async(std::launch::async, testThread);//Wrong!

It doesn’t work, the testThread will run, but the program will be blocked and waits until the testThread finished. What we want is making it run in a separate thread, parallel.

So the correct way will be:

auto result = std::async(std::launch::async, testThread);//Good

the result’s type is actually std::future<int>, we use auto for conveniency. int is the returned type of testThread function. So if it returns something else, for example void, the returned type will be std::future<void>.

2 Specify launch::async

std::async can launch in two modes, one is launch::deferred and the other is launch::async.

lauch::deferred is rarely used, it means the task testThread will only be launched when result.get() or result.wait() is called.

lauch::async is mostly used, it means the task testThread will immediately run and it will be forced to run in another thread.

3 get the result

As the beginning of this blog described, we don’t know when the result is ready when using std::thread.

For std::async, we use result.get() to get the result, and the get() will wait for the thread to finish and return the value:

int testThread()
{
	std::this_thread::sleep_for(std::chrono::seconds(3));
	std::cout << "slept for 3 second\n";
	return 1;
}
void main()
{
	auto result = std::async(std::launch::async, testThread);
        int returnedValue = result.get();//wait for 3 seconds.
        return;
}

In the above code block, the result.get() will wait until the testThread finished computing and get the returned value.

If we don’t call result.get() but do some other work:

auto result = std::async(std::launch::async, testThread);
//do something else for longer than 3 seconds
int returnedValue = result.get();

The result.get() will return the value immediately, because the testThread is already done and the returned value is already stored in the variable result, you can use the value any time you want.

4 result.get() can only be called once

Inside result.get(), it is actually calling result.wait() and move(). Once it is called, the returned value of testThread is moved out and no longer valid, we can call result.valid() to check:

auto result = std::async(std::launch::async, testThread);
//do something here
if(result.valid())//If you don't know get() is already called
{
	int returnedValue = result.get();    
}

Normally we don’t need to check the result.valid(), the best way to go is copying the returned value to another variable, in the above case int returnedValue.

Wangxin -->

Wangxin

I am algorithm engineer focused in computer vision, I know it will be more elegant to shut up and show my code, but I simply can't stop myself learning and explaining new things ...