开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第2天,点击查看活动详情
C++ 线程条件变量
使用C++线程可以很好的实现线程之间同步的问题。
使用条件变量需要 #include <condition_variable>
C++ 标准库中有两套实现 std::condition_variable和std::condition_variable_any, 两者都需要与一个互斥量一起才能工作;
- std::condition_variable 仅限于与 std::mutex 一起工作
- std::condition_variable_any 可以和任何满足最低标准的互斥量一起工作,但会产生额外的体积、性能、以及系统资源的开销
使用条件变量可以实现经典的
消费者和生产者问题,代码如下:
#include <iostream>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <unistd.h>
std::mutex mtx;
std::condition_variable condition;
std::queue<int> q;
void producer()
{
while(true)
{
int num = rand() % 10 + 1;
sleep(num);
std::lock_guard<std::mutex> lk(mtx);
q.push(num);
condition.notify_one();
}
}
void consumer()
{
while (true)
{
std::unique_lock<std::mutex> lk(mtx); // 1
// 当队列 q 为空等待通知
while(q.empty())
{
condition.wait(lk); // 2 阻塞等待通知
}
// condition.wait(lk, []{return !q.empty();}); // 该代码与 while... 作用相同
std::cout << "consumer:" << q.front() << std::endl;
q.pop();
}
}
int main()
{
std::thread t1(producer);
std::thread t2(consumer);
t1.join();
t2.join();
}
在上述消费者线程中,代码 2 处有有一次解锁(有多个消费者的时候不影响其他线程获取锁,否则锁会被当前的线程一直占有),等待通知后再重新加锁的过程。
-
notify_all() // 通知所有等待的线程
-
notify_one() // 通知一个等待线程
C++ 线程的期望
- std::future 使用
future可以用来接收线程的返回值,当我们需要拿到这个返回值可以直接调用 future 对象的 get() 方法, future 通常配合 std::async 配合使用 - std::shared_future 使用
shared_future可以多次调用 get() 方法, 而future仅能调用一次 get() 方法
std::async
-
std::async 区别于 std::thread, 当系统资源紧张的时候,thread 可能创建失败从而导致整程序的崩溃,async 创建异步任务,可能创建线程也可能不创建线程
future 的简单使用示例:
#include <iostream>
#include <future>
int func(int a, int b)
{
return a + b;
}
int main()
{
std::future<int> f = std::async(func, 3, 4);
std::cout << f.get() << std::endl;
}
控制 async 是否创建线程或者线程启动的时间
- std::launch::async 在一个新线程上启动
- std::launch::deferred 在 wait(), get() 的时候线程执行
代码示例:
std::future<int> f1 = std::async(std::launch::async, function, arg1, arg2); // 在新的线程上启动
f1.wait(); // 阻塞直至可以获取线程的返回值
std::future<int> f2 = std::asyncc(std::launch::deferred, function, arg1, arg2); // 在wait(), get() 的时候调用
f2.wait(); // 开始调用,
f2.get(); // 开始调用
std::future<int> f3 = std::asyncc(std::launch::async|std::launch::deferred, function, arg1, arg2);
C++ 时钟
-
std::chrono::system_clock::now() 返回系统的时间,system_clock 是不稳定的,因为其时钟可调,可能导致当前调用得到的时间早于之前得到的时间。
-
std::chrono::steady_clock::now() C++提供的稳定的时钟(steady_clock)
显示时间代码:
auto now = std::chrono::system_clock::now()
std::cout << std::chrono::duration<double/int/long>(now).count() << std::endl;
-
可接受超时的函数
ps: 为什么vscode markdown可以正常显示,掘金这里不可以正常显示
|类型/命名空间 | 函数 | 返回值|
| ---------- | ---- | ------ |
|std::this_thread[namespace] | sleep_for(duration)
sleep_until(time_point) | N/A |
|std::condition_variable 或
std::condition_variable_any| wait_for(lock, duration)
wait_until(lock, time_point) | std::cv_status::time_out 或
std::cv_status::no_timeout|
| | wait_for(lock, duration, predicate)
wait_until(lock, duration, predicate)| bool —— 当唤醒时,返回谓词的结果|
| std::timed_mutex 或
std::recursive_timed_mutex | try_lock_for(duration)
try_lock_until(time_point) | bool —— 获取锁时返回true,否则返回fasle |
| std::unique_lock | unique_lock(lockable, duration)
unique_lock(lockable, time_point) | N/A —— 对新构建的对象调用owns_lock();
当获取锁时返回true,否则返回false |
| | try_lock_for(duration)
try_lock_until(time_point) | bool —— 当获取锁时返回true,否则返回false |
|std::future或
std::shared_future |wait_for(duration) | 当等待超时,返回std::future_status::timeout |
|std::future或
std::shared_future | wait_until(time_point) | 当“期望”准备就绪时,返回std::future_status::ready
当“期望”持有一个为启动的延迟函数,返回std::future_status::deferred|
C++ promise/packaged_task
promise 和 packaged_task 都需要结合 future 使用;
promise 使用 promise:: set_value 方法去通知future
promoise使用实例代码:
#include <vector>
#include <thread>
#include <future>
#include <numeric>
#include <iostream>
#include <chrono>
void accumulate(std::vector<int>::iterator first,
std::vector<int>::iterator last,
std::promise<int> accumulate_promise)
{
int sum = std::accumulate(first, last, 0);
accumulate_promise.set_value(sum); // Notify future
}
void do_work(std::promise<void> barrier)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
barrier.set_value();
}
int main()
{
// Demonstrate using promise<int> to transmit a result between threads.
std::vector<int> numbers = { 1, 2, 3, 4, 5, 6 };
std::promise<int> accumulate_promise;
std::future<int> accumulate_future = accumulate_promise.get_future();
std::thread work_thread(accumulate, numbers.begin(), numbers.end(),
std::move(accumulate_promise));
// future::get() will wait until the future has a valid result and retrieves it.
// Calling wait() before get() is not needed
//accumulate_future.wait(); // wait for result
std::cout << "result=" << accumulate_future.get() << '\n';
work_thread.join(); // wait for thread completion
// Demonstrate using promise<void> to signal state between threads.
std::promise<void> barrier;
std::future<void> barrier_future = barrier.get_future();
std::thread new_work_thread(do_work, std::move(barrier));
barrier_future.wait();
new_work_thread.join();
}
packaged_task 用 future 来接收 task 的结果
使用示例代码:
#include <iostream>
#include <cmath>
#include <thread>
#include <future>
#include <functional>
// unique function to avoid disambiguating the std::pow overload set
int f(int x, int y) { return std::pow(x,y); }
void task_lambda()
{
std::packaged_task<int(int,int)> task([](int a, int b) {
return std::pow(a, b);
});
std::future<int> result = task.get_future();
task(2, 9);
std::cout << "task_lambda:\t" << result.get() << '\n';
}
void task_bind()
{
std::packaged_task<int()> task(std::bind(f, 2, 11));
std::future<int> result = task.get_future();
task();
std::cout << "task_bind:\t" << result.get() << '\n';
}
void task_thread()
{
std::packaged_task<int(int,int)> task(f);
std::future<int> result = task.get_future();
std::thread task_td(std::move(task), 2, 10);
task_td.join();
std::cout << "task_thread:\t" << result.get() << '\n';
}
int main()
{
task_lambda();
task_bind();
task_thread();
}