Библиотека многопоточности C++<thread>
C++11 представил поддержку многопоточности, с помощью<thread>библиотеки разработчики могут легко реализовать параллельную обработку в программах.
В этой статье будут представлены<thread>основные концепции, определения, синтаксис библиотеки и способы её использования для создания потоков и управления ими.
Поток — это наименьшая единица выполнения программы и наименьшая единица, которую операционная система может планировать для вычислений.
В многопоточных программах несколько потоков могут выполняться параллельно, повышая эффективность выполнения программы.
C++ <thread>Обзор библиотеки
<thread>Библиотека является частью стандартной библиотеки C++, предоставляя базовую функциональность для создания потоков и управления ими. Она включает следующие ключевые компоненты:
std::thread: представляет поток; он может создавать, запускать, ожидать завершения и уничтожать потоки.std::this_thread: предоставляет некоторые статические функции-члены для работы с текущим потоком.std::thread::id: уникальный идентификатор потока.
Создание потоков
Чтобы создать поток, необходимо создать экземплярstd::threadкласса и передать вызываемый объект (функцию, лямбда-выражение или функцию-член объекта) в качестве аргумента.
Пример
#include <thread>
void print_id(int id) {
std::cout << "ID: " << id << ", ID потока: " << std::this_thread::get_id() << std::endl;
}
int main() {
std::thread t1(print_id, 1);
std::thread t2(print_id, 2);
}
Запуск потоков
Создайтеstd::threadПосле создания объекта поток немедленно начнёт выполнение; можно вызватьjoin()метод, чтобы дождаться завершения потока.
t1.join(); t2.join();
Ожидание завершения потока
join()метод блокирует текущий поток до завершения выполнения вызванного потока.
Уничтожение потоков
Когда поток завершил выполнение, можно использоватьdetach()метод, чтобы отсоединить поток, или позволитьstd::threadобъекту выйти из области видимости и уничтожиться автоматически.
t1.detach(); // 线程将继续运行,但无法再被 join или detach
Пример: использование<thread>Создание параллельных вычислений
Ниже приведён пример использования<thread>Пример параллельных вычислений, реализованных с помощью библиотеки, вычисляющий сумму двух чисел.
Пример
#include <thread>
int sum = 0;
void add(int a, int b) {
sum += a + b;
}
int main() {
int a = 5;
int b = 10;
std::thread t1(add, a, b);
std::thread t2(add, a, b);
t1.join();
t2.join();
std::cout << "Сумма: " << sum << std::endl; // Результат вывода: Sum: 30
}
Результат вывода:
Sum: 30
В следующем примере мы создадим два потока, каждый из которых выполняет простую функцию, которая выводит сообщение и спит некоторое время:
Пример
#include <thread>
#include <chrono>
// Простая функция, выполняемая в потоке
void print_message(const std::string& message, int delay) {
std::this_thread::sleep_for(std::chrono::milliseconds(delay));
std::cout << message << std::endl;
}
int main() {
// Создайте два потока и выполните функцию print_message
std::thread t1(print_message, "Hello from thread 1", 1000);
std::thread t2(print_message, "Hello from thread 2", 500);
// Дождаться завершения потока t1
if (t1.joinable()) {
t1.join();
}
// Дождаться завершения потока t2
if (t2.joinable()) {
t2.join();
}
std::cout << "Main thread finished." << std::endl;
return 0;
}
Результат вывода:
Hello from thread 2 Hello from thread 1 Main thread finished.
Примечания
- Потокобезопасность: В многопоточной среде общие ресурсы требуют синхронизированного доступа, чтобы избежать гонок данных.
- Жизненный цикл потока: Убедитесь, что объект потока корректно обрабатывается после завершения выполнения потока, чтобы избежать утечек ресурсов.
Классы и функции
<thread>Библиотека содержит ряд классов и функций для создания, управления и синхронизации потоков.
Далее речь пойдёт о C++<thread>Подробное введение в библиотеку:
Основные компоненты
- std::thread
- std::mutex
- std::lock_guard
- std::unique_lock
- std::condition_variable
- std::future и std::promise
- std::async
std::thread
Класс std::thread используется для создания и управления потоками
Пример
#include <thread>
void print_hello() {
std::cout << "Привет из потока!" << std⟧::endl;
}
int main() {
std::thread t(print_hello);
t.join(); // Wait for thread t to finish
return 0;
}
Важно
- join(): Ожидать завершения потока.
- detach(): Перевести поток в фоновый режим и больше не ожидать его завершения.
- joinable(): Проверить, можно ли присоединить или отсоединить поток.
std::mutex
Класс std::mutex используется для синхронизации доступа к общим ресурсам.
Пример
#include <thread>
#include <mutex>
std::mutex mtx; // Создать глобальный объект mutex
int shared_resource = 0; // Общий ресурс
// Функция потока
void increment() {
std::lock_guard<std::mutex> lock(mtx); // Блокировка для обеспечения потокобезопасности
++shared_resource;
std::cout << "Значение shared_resource увеличено до " << shared_resource << std::endl;
// lock автоматически освобождается, когда lock_guard выходит из области видимости
}
int main() {
std::thread t1(increment);
std::thread t2(increment);
t1.join(); // Ожидание завершения потока t1
t2.join(); // Ожидание завершения потока t2
std::cout << "Итоговое значение shared_resource: " << shared_resource << std::endl;
return 0;
}
std::lock_guard
std::lock_guard — это RAII-менеджер блокировок, используемый для автоматического управления жизненным циклом блокировки.
Пример
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
std::lock_guard<std::mutex> lock(mtx);
std::cout << "Thread ID: " << id << std::endl;
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
std::unique_lock
std::unique_lock обеспечивает более гибкое управление блокировкой, чем std::lock_guard.
Пример
#include <thread>
#include <mutex>
std::mutex mtx;
void print_thread_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
std::cout << "Thread ID: " << id << std::endl;
lock.unlock(); // Можно разблокировать вручную
// ... другие операции
}
int main() {
std::thread t1(print_thread_id, 1);
std::thread t2(print_thread_id, 2);
t1.join();
t2.join();
return 0;
}
std::condition_variable
std::condition_variable используется для ожидания и уведомления между потоками.
Пример
#include <thread>
#include <mutex>
#include <condition_variable>
std::mutex mtx;
std::condition_variable cv;
bool ready = false;
void print_id(int id) {
std::unique_lock<std::mutex> lock(mtx);
cv.wait(lock, []{ return ready; });
std::cout << "Thread ID: " << id << std::endl;
}
void set_ready() {
std::unique_lock<std::mutex> lock(mtx);
ready = true;
cv.notify_all();
}
int main() {
std::thread t1(print_id, 1);
std::thread t2(print_id, 2);
std::this_thread::sleep_for(std::chrono::seconds(1));
set_ready();
t1.join();
t2.join();
return 0;
}
std::future и std::promise
std::future и std::promise используются для передачи результатов между потоками.
Пример
#include <thread>
#include <future>
void calculate_square(std::promise<int> && p, int x) {
p.set_value(x * x);
}
int main() {
std::promise<int> p;
std::future<int> f = p.get_future();
std::thread t(calculate_square, std::move(p), 5);
std::cout << "Square: " << f.get() << std::endl;
t.join();
return 0;
}
std::async
std::async используется для запуска асинхронных задач и возвращает std::future.
Пример
#include <future>
int calculate_square(int x) {
return x * x;
}
int main() {
std::future<int> result = std::async(calculate_square, 5);
std::cout << "Square: " << result.get() << std::endl;
return 0;
}
C++'s<thread>Библиотека предоставляет разработчикам мощную поддержку многопоточности. Благодаря введению в этой статье мы сможем понять основные концепции потоков и научиться использовать<thread>библиотеку для создания потоков и управления ими. В реальной разработке разумное использование многопоточности может значительно повысить производительность и отзывчивость программы.