SEP200

Threads

Summary

Introduction

Thread Class

Future Class

Introduction

Single-threaded Apps

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

Multi-threaded vs Multi-tasking

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)

Thread Class

Introduction

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*

Introduction

To create a thread, one can use the syntax:

std::thread(callable, list_arguments)

The callable can be:

  function pointer

  function object

  lambda expression

Function Pointer

Example

#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;
}

              

Join

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

Detach

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

Join and detach

Example

#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;
}

              

Functor

Example

#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;
}

              

Lambda Function

Example

#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;
}

              

Future

Introduction

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

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

promises and Futures

Example

#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;
}

              

promises

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)

Future

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

Suggested Reading

Multithreading (until Policy-Based Execution)

Thread Classes