После C++11 появилась стандартная библиотека потоков: std::thread.

Ранее некоторые компиляторы использовали флаг компиляции -std=c++11 для C++11

g++ -std=c++11 test.cpp 

Конструктор std::thread

Конструктор по умолчанию thread() noexcept;
Конструктор инициализации template <class Fn, class... Args>
explicit thread(Fn&& fn, Args&&... args);
Конструктор копирования [удалён] thread(const thread&) = delete;
Конструктор перемещения thread(thread&& x) noexcept;
  • Конструктор по умолчанию, создаёт пустойstd::threadобъект выполнения.
  • Конструктор инициализации, создаётstd::threadобъект, которыйstd::threadобъект может бытьjoinable, вновь созданный поток вызоветfnфункцию, параметры которойargsпредоставляются.
  • Конструктор копирования (удалён), что означает, чтоstd::threadобъекты не могут быть сконструированы копированием.
  • Конструктор перемещения. Конструктор перемещения (семантика перемещения — новое понятие в C++11, см. приложение для подробностей), после успешного вызоваxне представляет никакогоstd::threadобъект выполнения.

Примечание: может бытьjoinableизstd::threadобъекты должны быть присоединены главным потоком, прежде чем они будут уничтоженыjoinили установить их вdetached.

std::threadПримеры различных конструкторов приведены ниже:

#include <iostream>
#include <utility>
#include <thread>
#include <chrono>
#include <functional>
#include <atomic>

void f1(int n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread " << n << " executing\n";
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

void f2(int& n)
{
    for (int i = 0; i < 5; ++i) {
        std::cout << "Thread 2 executing\n";
        ++n;
        std::this_thread::sleep_for(std::chrono::milliseconds(10));
    }
}

int main()
{
    int n = 0;
    std::thread t1; // t1 is not a thread
    std::thread t2(f1, n + 1); // pass by value
    std::thread t3(f2, std::ref(n)); // pass by reference
    std::thread t4(std::move(t3)); // t4 is now running f2(). t3 is no longer a thread
    t2.join();
    t4.join();
    std::cout << "Final value of n is " << n << '\n';
}

операция присваивания std::thread

Операция перемещающего присваивания thread& operator=(thread&& rhs) noexcept;
Операция копирующего присваивания [удалена] thread& operator=(const thread&) = delete;
  • Операция перемещающего присваивания (1), если текущий объект неjoinable, должна быть передана rvalue-ссылка (rhs) вmoveоперацию присваивания; если текущий объект может бытьjoinable, затемterminate() будет вызвана, сообщая об ошибке.
  • Операция копирующего присваивания (2) удалена, поэтомуstd::threadобъекты не могут быть присвоены копированием.

См. пример ниже:

#include <stdio.h>
#include <stdlib.h>

#include <chrono>    // std::chrono::seconds
#include <iostream>  // std::cout
#include <thread>    // std::thread, std::this_thread::sleep_for

void thread_task(int n) {
    std::this_thread::sleep_for(std::chrono::seconds(n));
    std::cout << "hello thread "
        << std::this_thread::get_id()
        << " paused " << n << " seconds" << std::endl;
}

int main(int argc, const char *argv[])
{
    std::thread threads[5];
    std::cout << "Spawning 5 threads...\n";
    for (int i = 0; i < 5; i++) {
        threads[i] = std::thread(thread_task, i + 1);
    }
    std::cout << "Done spawning threads! Now wait for them to join\n";
    for (auto& t: threads) {
        t.join();
    }
    std::cout << "All threads joined.\n";

    return EXIT_SUCCESS;
}

Другие функции-члены

get_id: Получить идентификатор потока, возвращает объект типа std::thread::id. См. пример ниже:

#include <iostream>
#include <thread>
#include <chrono>

void foo()
{
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
  std::thread t1(foo);
  std::thread::id t1_id = t1.get_id();

  std::thread t2(foo);
  std::thread::id t2_id = t2.get_id();

  std::cout << "t1's id: " << t1_id << '\n';
  std::cout << "t2's id: " << t2_id << '\n';

  t1.join();
  t2.join();
}

joinable: Проверить, является ли поток присоединяемым. Проверить, представляет ли текущий объект потока активный исполняемый поток. Поток, созданный конструктором по умолчанию, нельзя присоединить. Кроме того, если поток завершил свою задачу, но не был присоединён, он всё ещё будет считаться активным исполняемым потоком и поэтому может быть присоединён.

#include <iostream>
#include <thread>
#include <chrono>

void foo()
{
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
  std::thread t;
  std::cout << "before starting, joinable: " << t.joinable() << '\n';

  t = std::thread(foo);
  std::cout << "after starting, joinable: " << t.joinable() << '\n';

  t.join();
}
join: Join 线程,调用该函数会阻塞当前线程,直到由 *this 所标示的线程执行完毕 join 才返回。

#include <iostream>
#include <thread>
#include <chrono>

void foo()
{
  // simulate expensive operation
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

void bar()
{
  // simulate expensive operation
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
  std::cout << "starting first helper...\n";
  std::thread helper1(foo);

  std::cout << "starting second helper...\n";
  std::thread helper2(bar);

  std::cout << "waiting for helpers to finish..." << std::endl;
  helper1.join();
  helper2.join();

  std::cout << "done!\n";
}

detach: Отсоединить поток. Отделить экземпляр выполнения, представленный текущим объектом потока, от объекта потока, чтобы поток мог выполняться независимо. После завершения выполнения потока выделенные ему ресурсы будут освобождены.

После вызова функции detach:

  • *thisон больше не представляет никакого экземпляра выполнения потока.
  • joinable() == false
  • get_id() == std::thread::id()

Кроме того, если возникает ошибка или joinable() == false, будет выброшено исключение std::system_error.

#include <iostream>
#include <chrono>
#include <thread>
 
void independentThread() 
{
    std::cout << "Starting concurrent thread.\n";
    std::this_thread::sleep_for(std::chrono::seconds(2));
    std::cout << "Exiting concurrent thread.\n";
}
 
void threadCaller() 
{
    std::cout << "Starting thread caller.\n";
    std::thread t(independentThread);
    t.detach();
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "Exiting thread caller.\n";
}
 
int main() 
{
    threadCaller();
    std::this_thread::sleep_for(std::chrono::seconds(5));
}

swap: Обменять потоки, обменивает нижележащие дескрипторы, представленные двумя объектами потоков.

#include <iostream>
#include <thread>
#include <chrono>

void foo()
{
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

void bar()
{
  std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
  std::thread t1(foo);
  std::thread t2(bar);

  std::cout << "thread 1 id: " << t1.get_id() << std::endl;
  std::cout << "thread 2 id: " << t2.get_id() << std::endl;

  std::swap(t1, t2);

  std::cout << "after std::swap(t1, t2):" << std::endl;
  std::cout << "thread 1 id: " << t1.get_id() << std::endl;
  std::cout << "thread 2 id: " << t2.get_id() << std::endl;

  t1.swap(t2);

  std::cout << "after t1.swap(t2):" << std::endl;
  std::cout << "thread 1 id: " << t1.get_id() << std::endl;
  std::cout << "thread 2 id: " << t2.get_id() << std::endl;

  t1.join();
  t2.join();
}

Результат выполнения выглядит следующим образом:

thread 1 id: 1892
thread 2 id: 2584
after std::swap(t1, t2):
thread 1 id: 2584
thread 2 id: 1892
after t1.swap(t2):
thread 1 id: 1892
thread 2 id: 2584

native_handle: Возвращает нативный дескриптор (поскольку реализация std::thread связана с операционной системой, эта функция возвращает дескриптор потока, связанный с конкретной реализацией std::thread; например, на POSIX-совместимых платформах (таких как Unix/Linux), это библиотека Pthread).

#include <thread>
#include <iostream>
#include <chrono>
#include <cstring>
#include <pthread.h>

std::mutex iomutex;
void f(int num)
{
  std::this_thread::sleep_for(std::chrono::seconds(1));

 sched_param sch;
 int policy; 
 pthread_getschedparam(pthread_self(), &policy, &sch);
 std::lock_guard<std::mutex> lk(iomutex);
 std::cout << "Thread " << num << " is executing at priority "
           << sch.sched_priority << '\n';
}

int main()
{
  std::thread t1(f, 1), t2(f, 2);

  sched_param sch;
  int policy; 
  pthread_getschedparam(t1.native_handle(), &policy, &sch);
  sch.sched_priority = 20;
  if(pthread_setschedparam(t1.native_handle(), SCHED_FIFO, &sch)) {
      std::cout << "Failed to setschedparam: " << std::strerror(errno) << '\n';
  }

  t1.join();
  t2.join();
}

Результат выполнения выглядит следующим образом:

Поток 2 выполняется с приоритетом 0 Поток 1 выполняется с приоритетом 20

hardware_concurrency [static]: Определяет характеристики аппаратного параллелизма, возвращает количество потоков, параллельно поддерживаемых реализацией потоков на текущей платформе, но возвращаемое значение — лишь системная подсказка.

#include <iostream>
#include <thread>

int main() {
  unsigned int n = std::thread::hardware_concurrency();
  std::cout << n << " concurrent threads are supported.\n";
}

Введение в связанные вспомогательные функции в пространстве имён std::this_thread

get_id: Получить идентификатор потока.

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

std::mutex g_display_mutex;

void foo()
{
  std::thread::id this_id = std::this_thread::get_id();

  g_display_mutex.lock();
  std::cout << "thread " << this_id << " sleeping...\n";
  g_display_mutex.unlock();

  std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main()
{
  std::thread t1(foo);
  std::thread t2(foo);

  t1.join();
  t2.join();
}

yield: Текущий поток уступает выполнение, и операционная система планирует продолжение выполнения другого потока.

#include <iostream>
#include <chrono>
#include <thread>

// "busy sleep" while suggesting that other threads run 
// for a small amount of time
void little_sleep(std::chrono::microseconds us)
{
  auto start = std::chrono::high_resolution_clock::now();
  auto end = start + us;
  do {
      std::this_thread::yield();
  } while (std::chrono::high_resolution_clock::now() < end);
}

int main()
{
  auto start = std::chrono::high_resolution_clock::now();

  little_sleep(std::chrono::microseconds(100));

  auto elapsed = std::chrono::high_resolution_clock::now() - start;
  std::cout << "waited for "
            << std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
            << " microseconds\n";
}

sleep_until: Поток засыпает до указанного момента времени, после чего поток снова пробуждается.

template< class Clock, class Duration >
void sleep_until( const std::chrono::time_point<Clock,Duration>& sleep_time );

sleep_for: Поток засыпает на указанный промежуток времени, после чего поток снова пробуждается. Однако из-за планирования потоков и других причин фактическое время сна может быть больше промежутка времени, представленного sleep_duration.

#include <iostream>
#include <chrono>
#include <thread>

int main()
{
  std::cout << "Hello waiter" << std::endl;
  std::chrono::milliseconds dura( 2000 );
  std::this_thread::sleep_for( dura );
  std::cout << "Waited 2000 ms\n";
}

Результат выполнения выглядит следующим образом:

Hello waiter
Waited 2000 ms

Источник: https://github.com/forhappy/Cplusplus-Concurrency-In-Practice/blob/master/zh/chapter3-Thread/Introduction-to-Thread.md