c++多线程
参考:https://www.cnblogs.com/haippy/p/3284540.html
1.1 condition_variable条件变量
条件变量是线程间通信的一种方式(共享全局变量实现),主要是一个线程阻塞,等待被另一个线程唤醒;条件变量存在于头文件<condition_variable>,常用函数如下:
-
构造函数:
td::condition_variable cv; //创建一个条件变量cv -
notify_one()/notify_all()
notify_one():唤醒正在阻塞的线程之一(最早阻塞的?) notify_all():唤醒所有阻塞的线程 -
wait函数:
void wait( std::unique_lock<std::mutex>& lock ); // 阻塞当前线程,等待被唤醒template< class Predicate > void wait(std::unique_lock<std::mutex>& lock, Predicate pred );关于pred有两点需要注意:
- 第一次执行到wait时,如果pred为false则阻塞,等待被唤醒;否则不阻塞
- 被notify被唤醒时,如果pred为false则继续阻塞;否则不阻塞
下面是一段示例代码:
#include <condition_variable>
#include <thread>
#include <mutex>
#include <stdio.h>
#include <atomic>
#include <chrono>
#include <iostream>
void condition_variable_test() {
std::condition_variable cv; // 创建条件变量
std::mutex lock_;
std::atomic<bool> running_{ true };
auto func = [&](int tid) {
printf("thread start, tid=%d\n", tid);
std::unique_lock<std::mutex> unique_lock_(lock_);
printf("%d: Wait Signal\n", tid);
// wait等待notify_one()或者notify_all()唤醒
cv.wait(unique_lock_, [&](){
printf("%d. 如果返回false,则继续等待,返回true退出等待\n", tid);
return !running_; // !running_为true(就退出等待),!running_为false就继续等待
});
printf("%d: Done\n", tid);
};
std::thread t0(func, 0); // 启动0号线程
std::this_thread::sleep_for(std::chrono::seconds(3)); //线程睡眠3s
printf("Notify one 1\n");
cv.notify_one(); //第一次唤醒cv信号变量
std::this_thread::sleep_for(std::chrono::seconds(3));
running_ = false;
printf("Notify one 2.\n");
cv.notify_one();//第二次唤醒cv信号变量
t0.join(); // 等待子线程结束
}
int main() {
condition_variable_test();
std::cin.get();
return 0;
}
下面代码自己实现一个条件变量:
#include <condition_variable>
#include <thread>
#include <mutex>
#include <stdio.h>
#include <atomic>
#include <chrono>
#include <iostream>
class MyConitionVariable {
public:
template<typename _Lock, typename _Predict>
void wait(_Lock& lock, const _Predict& p) {
while (!p()) {
wait(lock);
}
}
void notify_one() {
has_notify_signal_ = true;
}
private:
// 真实场景是,wait可以多个线程同时wait,而signal则可以是数组,每个线程都可以消费掉一个信号
template<typename _Lock>
void wait(_Lock& lock) {
lock.unlock();
while (!has_notify_signal_) {
std::this_thread::yield();
}
// 消费掉这个信号
has_notify_signal_ = false;
lock.lock();
}
private:
volatile bool has_notify_signal_ = false;
};
void condition_variable_test() {
//std::condition_variable cv; // 创建条件变量
MyConitionVariable cv;
std::mutex lock_;
std::atomic<bool> running_{ true };
auto func = [&](int tid) {
printf("thread start, tid=%d\n", tid);
std::unique_lock<std::mutex> unique_lock_(lock_);
printf("%d: Wait Signal\n", tid);
// wait等待notify_one()或者notify_all()唤醒
cv.wait(unique_lock_, [&](){
printf("%d. 如果返回false,则继续等待,返回true退出等待\n", tid);
return !running_; // !running_为true(就退出等待),!running_为false就继续等待
});
printf("%d: Done\n", tid);
};
std::thread t0(func, 0); // 启动0号线程
std::this_thread::sleep_for(std::chrono::seconds(3)); //线程睡眠3s
printf("Notify one 1\n");
cv.notify_one(); //第一次唤醒cv信号变量
std::this_thread::sleep_for(std::chrono::seconds(3));
running_ = false;
printf("Notify one 2.\n");
cv.notify_one();//第二次唤醒cv信号变量
t0.join(); // 等待子线程结束
}
int main() {
condition_variable_test();
std::cin.get();
return 0;
}
1.2 promise和future
https://cplusplus.com/reference/future/
future和promise的作用是在不同线程之间传递数据。
future存在于头文件<future>中,<future> 头文件中包含了以下几个类和函数:
- Providers 类:std::promise, std::package_task
- Futures 类:std::future, shared_future.
- Providers 函数:std::async()
- 其他类型:std::future_error, std::future_errc, std::future_status, std::launch.
![]()
std::promise的get_future()函数返回一个std::future对象,随后将std::promise传递给其他线程,std::promise在这个线程中设置的值,能被另外一个线程中与之关联的future对象获取到。通过这两个对象能实现共享状态值,即线程间通信。(文档原文:A promise is an object that can store a value of type T to be retrieved by a future object (possibly in another thread), offering a synchronization point.)
std::promise<int> prom; // 生成一个 std::promise<int> 对象.
std::future<int> fut = prom.get_future(); // prom返回一个future对象, 与之关联.
其通信机制如下:

下面是一段示例代码:
#include <iostream>
#include <future>
#include <thread>
void get_number(std::promise<int>& prom) {
prom.set_value(10);
}
void test_future() {
std::promise<int> prom;
std::future<int> fut = prom.get_future();
std::thread t0(get_number, std::ref(prom));
int number = fut.get();
t0.join();
std::cout << "Get number from thread " << t0.get_id() << " : " << number << std::endl;
}
std::future和std::shared_future区别
std::future的get()成员函数是转移数据所有权;std::shared_future的get()成员函数是复制数据。 因此: future对象的get()只能调用一次;无法实现多个线程等待同一个异步线程,一旦其中一个线程获取了异步线程的返回值,其他线程就无法再次获取。 std::shared_future对象的get()可以调用多次;可以实现多个线程等待同一个异步线程,每个线程都可以获取异步线程的返回值。下面是一段示例代码:
void test_shared_future() {
std::promise<int> prom;
std::shared_future<int> fut = prom.get_future();
std::thread(
[&]() {
printf("Async thread start\n");
std::this_thread::sleep_for(std::chrono::seconds(2));
prom.set_value(10);
}
).detach();
printf("wait value\n");
printf("Get value: %d\n", fut.get());
}
1.3 生产者和消费者模式
通过c++多线程,我们可以实现生产者和消费者模式。比如我们进行部署深度模型进行推理时,一个线程负责解码视频,提交图片到任务队列,一个线程负责从队列中取出图片,进行批量推理。下面是一个生产者消费者模式示例代码:
infer.hpp:
#include <thread>
#include <future>
#include <stdio.h>
#include <string>
#include <atomic>
#include <mutex>
#include <condition_variable>
#include <queue>
struct Job {
std::shared_ptr < std::promise<std::string>> prom; // 智能指针
std::string input;
};
class ProviderConsumer {
public:
std::shared_future<std::string> provide(const std::string& input);
bool startup();
void stop();
private:
void consume(std::promise<bool>& pro);
private:
std::atomic<bool> running_{ false };
std::thread worker_thread_;
std::mutex lock_;
std::condition_variable cv_;
std::queue<Job> jobs_;
};
infer.cpp:
#include "infer.hpp"
#include <stdio.h>
#include <vector>
bool ProviderConsumer::startup() {
running_ = true;
std::promise<bool> pro;
worker_thread_ = std::thread(&ProviderConsumer::consume, this, std::ref(pro));
return pro.get_future().get(); // 是否启动成功
}
std::shared_future<std::string> ProviderConsumer::provide(const std::string& input) {
Job job;
job.input = input;
job.prom.reset(new std::promise<std::string>()); //智能指针指向一个promise<string>对象
std::shared_future<std::string> fut = job.prom->get_future();
{
std::lock_guard<std::mutex> l(lock_);
jobs_.emplace(std::move(job)); // 生产者获取锁,向任务队列里面添加任务
}
cv_.notify_one(); // 唤醒阻塞的消费者
return fut;
}
void ProviderConsumer::consume(std::promise<bool>& pro) {
pro.set_value(true); // 消费者启动成功
std::vector<Job> fetched_jobs;
while (running_) {
{
std::unique_lock<std::mutex> l(lock_);
cv_.wait(l, [&]() {
return !running_ || !jobs_.empty();
}); // 收到notify信号后,如果不在运行,或者jobs_不为空时,退出阻塞继续执行
if (!running_) break; // 如果 不在运行 就直接结束循环
//从任务队列jobs_里面取出5个job,放到fetched_jobs
int batch_size = 5;
for (int i = 0; i < batch_size && !jobs_.empty(); i++) {
fetched_jobs.emplace_back(std::move(jobs_.front()));
jobs_.pop();
}
}
// 进行批量处理
for (auto& job : fetched_jobs) {
job.prom->set_value(job.input + "__processed");
}
fetched_jobs.clear();
}
printf("Consume work Done\n");
}
void ProviderConsumer::stop() {
if (running_) {
running_ = false;
cv_.notify_one();
}
if (worker_thread_.joinable()) {
worker_thread_.join();
}
}
main.cpp:
#include <iostream>
#include "infer.hpp"
void test_provider_consumer() {
ProviderConsumer p;
if (p.startup()) {
for (int i = 0; i < 10; i++) {
auto fut = p.provide(std::string("job")+std::to_string(i));
std::cout << "Get result: " << fut.get() << std::endl;
}
p.stop();
}
}
int main() {
test_provider_consumer();
std::cin.get();
return 0;
}
执行结果如下:



浙公网安备 33010602011771号