1、std::promise和std::future注意用来在线程间传递数据(不用手工同步来传递数据)。
2、在之前通过传递引用来传递数据,也能达到上述效果,但是需要手动同步,否则获取到不可预测的结果。
#include <iostream> #include <thread> void func(std::vector<int>& myVector,int& result) { auto it = std::max_element(myVector.begin(),myVector.end()); result = *it; } int main() { int result = 0; std::vector<int> myVector{1,2,3,6,7,8,99,12}; std::thread thread01(&func,std::ref(myVector),std::ref(result));//创建一个子线程,调用func,并把结果放引用参数中 thread01.join();//这里要手动同步,等待线程结束,才能下一句正确打印 std::cout << result << std::endl;//如果上面没有join,就会得到错误的结果。 return 0; }3、一种更安全的线程间传递数据的方式,使用promise设定值,使用promise.get_future,关联futrue对象,通过future.get()来获取promise设定的值,而且future的get自动同步promise,如果promise的set未完成,会自动阻塞并等待,直到set完成。
//更好的传递数据的方式 #include <iostream> #include <thread> #include <vector> #include <chrono> #include <future> #include <algorithm> void func(std::vector<int>& myVector,std::promise<int>& pro) { std::this_thread::sleep_for(std::chrono::seconds(2));//模拟子线程执行需要2秒 auto it = std::max_element(myVector.begin(),myVector.end()); pro.set_value(*it);//子线程用来设定结果 } int main() { std::vector<int> myVector{1,3,6,7,8,2,4}; std::promise<int> pro;//创建一个promise对象 std::future<int> fut = pro.get_future();//创建一个future对象,并关联到promise std::thread thread01(&func,std::ref(myVector),std::ref(pro)); int result = fut.get();//会自动等待promise.set()结束 std::cout << result << std::endl; thread01.join(); return 0; }4、在函数参数中,type&&表示参数可以接受一个右值引用对象,即参数可以接受一个临时对象,针对不可复制对象传递参数时,需要使用std::move方式传递,转移对象的所有权。type&表示接受一个引用对象,如果对象也是不可复制对象,需要使用std::ref()函数,将对象进行包装为左值引用对象。
#include <iostream> #include <future> #include <thread> #include <vector> #include <algorithm> #include <chrono> //线程函数 void find_max(const std::vector<int>& vec, std::promise<int>&& prom)//接受一个promise右值引用对象 //void find_max(const std::vector<int>& vec, std::promise<int>& prom)//接受一个promise引用对象 { std::this_thread::sleep_for(std::chrono::seconds(2)); try { auto it = std::max_element(vec.begin(), vec.end()); prom.set_value(*it);//设置结果 } catch (...) { prom.set_exception(std::current_exception());//设置异常结果 } } void findMin(std::vector<int>& myVector,int& result) { auto it = std::min_element(myVector.begin(), myVector.end()); result = *it; } int main() { std::vector<int> data{1,5,3,9,2}; std::promise<int> prom;//主线程创建一个promise对象 std::future<int> fut = prom.get_future();//主线程通过调用get_future()获得与promise对象相关联的future对象 std::thread worker(find_max, std::cref(data), std::move(prom));//创建一个子线程,传递数据和promise对象的右值引用 //std::thread worker(&find_max, std::ref(data), std::ref(prom));//传递promise的左值引用对象 int result = fut.get();//主线程通过调用future对象的get()方法获取结果,如果子线程未完成计算,主线程将会阻塞等待 std::cout << "Max element: " << result << std::endl; int result2 = 0; std::thread thread02(&findMin, std::ref(data), std::ref(result2)); thread02.join();//必须手动同步线程,等待线程完成后才能访问result2 std::cout << "Min element: " << result2 << std::endl; worker.join(); return 0; }5、以上2种方式,都可以完成线程之间数据的传递,推荐使用promise和future的方式,这样自动线程同步;而且不仅提供了设定值,而且异常时,可以设定异常,promise.set_exception()来设定异常;而且当不需要阻塞时,可以使用wait_for() wait_until()来设定等待的时间,特别适用于生产者消费者模式,但注意的是promise只能set_value一次,future也只能get一次。
6、鉴于这种线程间通信传递数据方式,只适合于“单次、异步任务的结果传递”,不适合“持续生成和消费”的情形。
C++原生支持持续生成和消费者模式。如下:
#include <iostream> #include <thread> #include <queue> #include <mutex> #include <condition_variable> #include <chrono> // 简单的线程安全阻塞队列 template<typename T> class BlockingQueue { private: std::queue<T> queue_; std::mutex mtx_; std::condition_variable cv_producer_; // 通知生产者(队列不满) std::condition_variable cv_consumer_; // 通知消费者(队列不空) size_t max_size_; public: BlockingQueue(size_t max_size) : max_size_(max_size) {} // 生产者:放入数据。如果队列满了就阻塞等待 void push(T item) { std::unique_lock<std::mutex> lock(mtx_); cv_producer_.wait(lock, [this] { return queue_.size() < max_size_; }); queue_.push(std::move(item)); std::cout << "[生产] 生产了一个数据,队列大小: " << queue_.size() << std::endl; cv_consumer_.notify_one(); // 叫醒一个正在等待的消费者 } // 消费者:取出数据。如果队列空了就阻塞等待 T pop() { std::unique_lock<std::mutex> lock(mtx_); cv_consumer_.wait(lock, [this] { return !queue_.empty(); }); T item = std::move(queue_.front()); queue_.pop(); std::cout << "[消费] 消费了一个数据,队列剩余: " << queue_.size() << std::endl; cv_producer_.notify_one(); // 叫醒一个正在等待的生产者 return item; } }; // 使用示例 int main() { BlockingQueue<int> bq(5); // 容量为5的队列 // 生产者线程 std::thread producer([&bq] { for (int i = 0; i < 20; ++i) { bq.push(i); std::this_thread::sleep_for(std::chrono::milliseconds(50)); } }); // 消费者线程 std::thread consumer([&bq] { for (int i = 0; i < 20; ++i) { int val = bq.pop(); std::this_thread::sleep_for(std::chrono::milliseconds(150)); // 消费慢一点 } }); producer.join(); consumer.join(); return 0; }