Introduction
Thread Class
Future Class
Up until now, our applications always follow a single sequence of steps (thread)
Only one line of code runs at any given time
This poses two problems:
→ Blocking operations halt execution
→ Parallel algorithms are executed in serial fashion
A multi-threaded application is an application in which multiple lines of code run concurrently, possibly using different CPUs
In this case, the application itself is the one managing its threads
This is not the same as multi-tasking, which is normally managed by the operating system (OS)
The C++11 standard included a thread library that contains a class called thread
Before, one needed to use system specific libraries
A thread object represents an actual thread of execution
main()
runs concurrently with its threads
If main()
terminates, its threads are also terminated*
To create a thread, one can use the syntax:
std::thread(callable, list_arguments)
The callable
can be:
→ function pointer
→ function object
→ lambda expression
#include <iostream>
#include <thread>
#include <string>
#include <chrono>
void print(std::string name, int interval)
{
for (int i = 0; i < 10; ++i)
{
std::cout << "Message " << i
<< " from thread: "
<< name << std::endl;
std::this_thread::sleep_for(
std::chrono::seconds(interval));
}
}
int main()
{
std::thread t1(print, "fast", 1);
std::thread t2(print, "slow", 5);
t1.join();
t2.join();
std::cout << "Main thread finished" << std::endl;
return 0;
}
The join()
method halts execution of the calling thread, until the thread that called it is finished
If the calling thread is main()
, it halts the execution of main()
Without join()
, one cannot guarantee that a thread is finished before its calling thread
If a thread is supposed to run on the background, you can use the detach
method
Once a thread is detached, it no longer can be "joined"
A deteached thread can be suddenly terminated without resulting in an error, but there could be unfinished tasks or corrupted data
#include <iostream>
#include <thread>
#include <string>
#include <chrono>
void print(std::string name, int interval)
{
for (int i = 0; i < 10; ++i)
{
std::cout << "Message " << i
<< " from thread: "
<< name << std::endl;
std::this_thread::sleep_for(
std::chrono::seconds(interval));
}
}
int main()
{
std::thread t1(print, "fast", 1);
std::thread t2(print, "slow", 5);
t2.detach();
t1.join();
std::cout << "Main thread finished" << std::endl;
return 0;
}
#include <iostream>
#include <thread>
#include <chrono>
class Functor
{
public:
void operator()(int id, int duration) const
{
for (int i = 0; i < duration; ++i)
{
std::this_thread::sleep_for(
std::chrono::seconds(1));
std::cout << "Thread " << id << " running: "
<< i + 1 << " seconds" << std::endl;
}
}
};
int main()
{
Functor obj;
std::thread t1(obj, 1, 3);
std::thread t2(obj, 2, 5);
t1.join();
t2.join();
std::cout << "Main thread finished" << std::endl;
return 0;
}
#include <iostream>
#include <thread>
#include <chrono>
int main()
{
std::thread t1([](int id, int duration)
{
for (int i = 0; i < duration; ++i)
{
std::this_thread::sleep_for(
std::chrono::seconds(1));
std::cout << "Thread " << id << " running: "
<< i + 1 << " seconds" << std::endl;
} }, 1, 3);
t1.join();
std::cout << "Main thread finished" << std::endl;
return 0;
}
What if one thread needs to pass a value to another thread?
How can we guarantee that the value has already been calculated to pass it forward?
The future
library solves this issue by using promises and futures
A promise is a value that will be sent to another thread
A future is a value that is consumed from anoter thread
Once a promise is fulfilled, a future can take place
#include <iostream>
#include <thread>
#include <future>
#include <chrono>
void producer(std::promise<int> prom)
{
std::this_thread::sleep_for(std::chrono::seconds(5));
int value = 42;
std::cout << "Producer thread produced value: "
<< value << std::endl;
prom.set_value(value);
}
void consumer(std::future<int> fut)
{
std::cout << "Consumer thread waiting for value..."
<< std::endl;
int value = fut.get();
std::cout << "Consumer thread received value: "
<< value << std::endl;
}
int main()
{
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread producerThread(producer, std::move(prom));
std::thread consumerThread(consumer, std::move(fut));
producerThread.join();
consumerThread.join();
std::cout << "Main thread finishing" << std::endl;
return 0;
}
To create a promise, you can use the syntax:
std::promise<TYPE> promise_name
To set, or fulfill a promise, you can use the syntax:
promise_name.set_value(value)
To pass ownership of a promise from one thread to another, use std::move(promise)
To create a future, and pair it to an existing promise, you can use the syntax:
std::future<TYPE> future_name = promise_name.get_future()
To set a variable with a value coming from a future, you can use the syntax:
variable_name = future_name.get()
The get()
method is a blocking method; execution is halted until a promise is fulfilled
Multithreading (until Policy-Based Execution)