C++ Threads Cookbook

API для многопоточности появился в С++ начиная со стандарта C++11.

Создание и запуск потока

Запуск потока выполняется при его создании.

#include <iostream>
#include <thread>

void foo()
{
    for (int i = 0; i < 10; i++)
    {
        std::cout << "foo" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

void bar()
{
    using namespace std::chrono_literals;
    for (int i = 0; i < 10; i++)
    {
        std::cout << "bar" << std::endl;
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main(int argc, char** argv)
{
    // Functions, lambda expressions, function objects and even class member functions
    // can be passed as parameters to theread constructor.
   
    // Variable number of parameters can be passed to theread constructor
    // because the counstructor is a variadic template.

    std::thread thread1(foo);
    std::thread thread2(bar);

    thread1.join();
    thread2.join();

    return 0;
}

Интересные штуки:

  • std::thread
  • std::thread::join
  • std::this_thread
  • std::this_thread::sleep_for

Получение результата работы потока или исключения, произошедшего в нём

#include <iostream>
#include <thread>
#include <stdexcept>

int foo(int n)
{
    if (n > 5)
        throw std::exception("Error!!!");

    int sum = 0;
    for (int i = 1; i <= n; i++)
        sum += i;
    return sum;
}

void thread_function(
    int param,
    int& result,
    std::exception_ptr& error)
{
    try
    {
        result = foo(param);
    }
    catch (...)
    {
        error = std::current_exception();
    }
}

int main(int argc, char** argv)
{
    int param = 0;
    int result = 0;
    std::exception_ptr error;

    std::cin >> param;

    std::thread thr(
        thread_function,
        param, std::ref(result),
        std::ref(error));
    thr.join();

    if (error)
    {
        try
        {
            std::rethrow_exception(error);
        }
        catch (const std::exception ex)
        {
            std::cout << ex.what() << std::endl;
        }
    }
    else
        std::cout << result;

    return 0;
}

Интересные штуки:

  • std::exception_ptr
  • std::rethrow_exception
  • std::ref

Атомарные операции

#include <atomic>

void thread_safe_increment(
    std::atomic<int>& value)
{
    value += 10;
}

void non_thread_safe_increment(
    int& value)
{
    value += 10;
}

Интересные штуки:

  • std::atomic<T>

Mutex

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutual_exclusion;

void foo()
{
    mutual_exclusion.lock();
    try
    {
        for (int i = 0; i < 10; i++)
        {
            std::cout << "foo" << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }

        mutual_exclusion.unlock();
    }
    catch (...)
    {
        mutual_exclusion.unlock();
    }
}

void bar()
{
    mutual_exclusion.lock();
    try
    {
        for (int i = 0; i < 10; i++)
        {
            std::cout << "bar" << std::endl;
            std::this_thread::sleep_for(std::chrono::milliseconds(1));
        }

        mutual_exclusion.unlock();
    }
    catch (...)
    {
        mutual_exclusion.unlock();
    }
}

int main(int argc, char** argv)
{
    std::thread thread1(foo);
    std::thread thread2(bar);
    thread1.join();
    thread2.join();

    return 0;
}

Интересные штуки:

  • std::mutex
  • std::recursive_mutex
  • std::mutex::lock
  • std::mutex::try_lock
  • std::mutex::unlock
  • std::timed_mutex
  • std::recursive_timed_mutex
  • std::shared_timed_mutex

lock_guard (RAII style)

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutual_exclusion;

void foo()
{
    std::lock_guard<std::mutex> lock(mutual_exclusion);
    for (int i = 0; i < 10; i++)
    {
        std::cout << "foo" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

void bar()
{
    std::lock_guard<std::mutex> lock(mutual_exclusion);
    for (int i = 0; i < 10; i++)
    {
        std::cout << "bar" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

int main(int argc, char** argv)
{
    std::thread thread1(foo);
    std::thread thread2(bar);
    thread1.join();
    thread2.join();

    return 0;
}

Интересные штуки:

  • std::lock_guard<T>
  • std::unique_lock<T>
  • std::shared_lock<T>

Захват нескольких lock’ов

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutual_exclusion1;
std::mutex mutual_exclusion2;

void foo()
{
    std::unique_lock<std::mutex> lock1(mutual_exclusion1, std::defer_lock_t());
    std::unique_lock<std::mutex> lock2(mutual_exclusion2, std::defer_lock_t());
    std::lock(lock1, lock2);
    for (int i = 0; i < 10; i++)
    {
        std::cout << "foo" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

void bar()
{
    std::unique_lock<std::mutex> lock1(mutual_exclusion1, std::defer_lock_t());
    std::unique_lock<std::mutex> lock2(mutual_exclusion2, std::defer_lock_t());
    std::lock(lock1, lock2);
    for (int i = 0; i < 10; i++)
    {
        std::cout << "bar" << std::endl;
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
    }
}

int main(int argc, char** argv)
{
    std::thread thread1(foo);
    std::thread thread2(bar);
    thread1.join();
    thread2.join();

    return 0;
}

Интересные штуки:

  • std::uniqie_lock<T>
  • std::lock

Conditional Variables

Conditional Variable — это средство синхронизации потоков, которое аналогично Autoreset Event, с которым я знаком по опыту программирования в .NET. Служат conditional variables для того, чтобы подать сигнал потоку, ожидающему событие, о произошедшем событии.

#include <iostream>
#include <thread>
#include <mutex>

std::mutex mutual_exclusion;
std::condition_variable cond_var;

void working_function()
{
    std::unique_lock<std::mutex> lock(mutual_exclusion);
    while (true)
    {
        cond_var.wait(lock);
        std::cout << "working" << std::endl;
    }
}

int main(int argc, char** argv)
{
    std::thread thr(working_function);

    while (true)
    {
        std::this_thread::sleep_for(std::chrono::milliseconds(1));
        std::unique_lock<std::mutex> lock(mutual_exclusion);

        std::cout << "Do some work? (y/n): ";
        char yes_no = 'n';
        std::cin >> yes_no;
        if (yes_no == 'y')
            cond_var.notify_one();
    }

    return 0;
}

Интересные штуки:

  • std::condition_variable
  • std::condition_variable::wait
  • std::condition_variable::notify_one
  • std::condition_variable::notify_all

async, packaged_task, future, promise

#include <iostream>
#include <future>

int working_function(int n)
{
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        sum += i;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::cout << "working" << std::endl;
    }
    return sum;
}

int main(int argc, char** argv)
{
    std::packaged_task<int(int)> task(working_function);
    auto fut = task.get_future();
    task(50);
    std::cout << fut.get();

    return 0;
}

Интересные штуки:

  • std::packaged_task
  • std::packaged_task::get_future
  • std::future::get
#include <iostream>
#include <future>

int working_function(int n)
{
    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        sum += i;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::cout << "working" << std::endl;
    }
    return sum;
}

int main(int argc, char** argv)
{
    auto fut = std::async(std::launch::async, working_function, 50);
    std::cout << fut.get();

    return 0;
}

Интересные штуки:

  • std::async
#include <iostream>
#include <future>
#include <stdexcept>

int working_function(int n)
{
    if (n > 100)
        throw std::exception("Error!!!");

    int sum = 0;
    for (int i = 0; i < n; i++)
    {
        sum += i;
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        std::cout << "working" << std::endl;
    }
    return sum;
}

int main(int argc, char** argv)
{
    auto fut = std::async(std::launch::async, working_function, 200);

    try
    {
        std::cout << fut.get();
    }
    catch (const std::exception& ex)
    {
        std::cout << ex.what();
    }

    return 0;
}

Добавить комментарий

Ваш адрес email не будет опубликован.