线程间的同步操作很常见,C++ 标准库专门为之提供了处理工具:条件变量和 future。另外,并发技术规约还提供了新式的同步工具:线程闩(latch)和线程卡(barrier)。
让线程休眠100毫秒:
std::this_thread::sleep_for(std::chrono::milliseconds(100));
凭借条件变量等待条件成立
std::mutex mut;
std::queue data_queue;
std::condition_variable data_cond;
void data_preparation_thread() // 由线程乙运行
{
while(more_data_to_prepare())
{
data_chunk const data = prepare_data();
{
std::lock_guard lk(mut);
data_queue.push(data);
}
data_cond.notify_one();
}
}
void data_processing_thread() // 由线程甲运行
{
while(true)
{
std::unique_lock lk(mut);
data_cond.wait(lk, []{ return !data_queue.empty(); });
data_chunk data = data_queue.front();
dara_queue.pop();
lk.unlock();
process(data);
if(is_last_chunk(data))
break;
}
}
利用条件变量构建线程安全的队列
#include
#include
#include
#include
template
class threadsafe_queue
{
private:
mutable std::mutex mut; // 互斥必须用 mutable 修饰(针对 const 对象,准许其数据成员发生变动)
std::queue data_queue;
std::condition_variable data_cond;
public:
threadsafe_queue()
{}
threadsafe_queue(threadsafe_queue const& other)
{
std::lock_guard lk(other.mut);
data_queue = other.data_queue;
}
void push(T new_value)
{
std::lock_guard lk(mut);
data_queue.push(new_value);
data_cond.notify_one();
}
void wait_and_pop(T& value)
{
std::unique_lock lk(mut);
data_cond.wait(lk, [this]{ return !data_queue.empty(); });
value = data_queue.front();
data_queue.pop();
}
std::shared_ptr wait_and_pop()
{
std::unique_lock lk(mut);
data_cond.wait(lk, [this]{ return !data_queue.empty(); });
std::shared_ptr res(std::make_shared(data_queue.front());
data_queue.pop();
return res;
}
bool try_pop(T& value)
{
std::lock_guard lk(mut);
if(data_queue.empty())
return false;
value = data_queue.front();
data_queue.pop();
return true;
}
std::shared_ptr try_pop()
{
std::lock_guard lk(mut);
if(data_queue.empty())
return std::shared_ptr();
std::shared_ptr res(std::make_shared(data_queue.front());
data_queue.pop();
return res;
}
bool empty() const
{
std::lock_guard lk(mut);
return data_queue.empty();
}
}
使用 future 等待一次性事件发生
从后台任务返回值
#include
#include
int find_the_answer_to_ltuae();
void do_other_stuff();
int main()
{
std::future the_answer = std::async(find_the_answer_to_ltuae);
do_other_stuff();
std::cout << "The answer is " << the_answer.get() << std::endl;
}
#include
#include
struct X
{
void foo(int, std::string const&);
std::string bar(std::string const&);
};
X x;
auto f1 = std::async(&X::foo, &x, 42, "hello"); // ①
auto f2 = std::async(&X::bar, x, "goodbye"); // ②
struct Y
{
double operator() (double);
}
Y y;
auto f3 = std::async(Y(), 3.141); // ③
auto f4 = std::async(std::ref(y), 2.718); // 调用 y(2.718)
X baz(X&);
std::async(baz, std::ref(x)); // 调用 baz(x)
class move_only
{
public:
move_only();
move_only(move_only&&);
move_only(move_only const&) = delete;
move_only& operator=(move_only&&);
move_only& operator=(move_only const&) = delete;
void operator() ();
};
auto f5 = std::async(move_only()); // ④
auto f6 = std::async(std::launch::async, Y(), 1.2); // 运行新线程
ayto f7 = std::async(std::launch::deferred, baz, std::ref(x)); // 在 wait() 或 get() 内部运行任务函数
auto f8 = std::async(std::launch::deferred | std::launch::async, baz, std::ref(x)); // 交由实现自行选择运行方式
auto f9 = std::async(baz, std::ref(x)); // 交由实现自行选择运行方式
f7.wait(); // 任务函数调用被延后到这里才运行
关联 future 实例和任务
// 定义特化的 sstd::packaged_task<> 类模板(定义不完全,只列出部分代码)
template<>
class packaged_task*, int)>
{
public:
template
explicit packaged_task(Callable&& f);
std::future get_future();
void operator() (std::vector*, int);
}
#include
#include
#include
#include
#include
std::mutex m;
std::deque> tasks;
bool gui_shutdown_message_received();
void get_and_process_gui_message();
void gui_thread()
{
while(!gui_shutdown_message_received())
{
get_and_process_gui_message();
std::packaged_task task;
{
std::lock_guard lk(m);
if(tasks.empty())
continue;
task = std::move(tasks.front());
tasks.pop_front();
}
task();
}
}
std::thread gui_bg_thread(gui_thread);
template
std::future post_task_for_gui_thread(Func f)
{
std::packaged_task task(f);
std::future res = task.get_future();
std::lock_guard lk(m);
tasks.push_back(std::move(task));
return res;
}
创建 std::promise
#include
void process_connections(connection_set& connections)
{
while(!done(connections))
{
for(connection_iterator connection = connections.begin(), end = connections.end(); connection != end; ++connection)
{
if(connection->has_incoming_data())
{
data_packet data = connection->incoming();
std::promise& p = connection->get_promise(data.id);
p.set_value(data.payload);
}
if(connection->has_outgoing_data())
{
outgoing_packet data = connection->top_of_outgoing_queue();
connection->send(data.payload);
data.promise.set_value(true);
}
}
}
}
将异常保存到 future 中
extern std::promise some_promise;
try
{
some_promise.set_value(calculate_value());
}
catch(...)
{
some_promise.set_exception(std::current_exception());
}
some_promise.set_exception(std::make_exception_ptr(std::logic_error("foo ")));
多个线程一起等待
std::promise p;
std::future f(p.get_future());
assert(f.valid()); // future 对象 f 有效
std::shared_future sf(std::move(f));
assert(!f.valid()); // 对象 f 不再有效
assert(sf.valid()); // 对象 sf 开始生效
std::promise p;
td::shared_future sf(p.get_future()); // 隐式转移归属权
std::promise< std::map::iterator> p;
auto sf = p.get_future().share();
限时等待
时钟类
时长类
using namespace std::chrono_literals;
auto one_day = 24h;
auto half_an_hour = 30min;
auto max_time_between_messages = 30ms;
std::chrono::milliseconds ms(54802);
std::chrono::seconds s = std::chrono::duration_cast(ms);
std::future f = std::async(some_task);
if(f.wait_for(std::chrono::milliseconds(35)) == std::future_status::ready)
do_something_with(f.get());
时间点类
auto start = std::chrono::high_resolution_clock::now();
do_something();
auto stop = std::chrono::high_resolution_clock::now();
std::cout << "do_something() took "
<< std::chrono::duration(stop-start).count()
<< " seconds" << std::endl;
#include
#include
#include
std::condition_variable cv;
bool done;
std::mutex m;
bool wait_stop()
{
auto const timeout = std::chrono::steady_clock::now() + std::chrono::milliseconds(500);
std::unique_lock lk(m);
while(!done)
{
if(cv.wait_until(lk, timeout) == std::cv_status::timeout)
break;
}
return done;
}
接收超时时限的函数
C++ 标准库中接受超时时限的函数:
类/名字空间 | 函数 | 返回值 |
---|---|---|
std::this_thread 名字空间 | sleep_for(duration) sleep_until(time_point) |
无 |
std::condition_variable 或 std::condition_variable_any wait_for(lock,duration) wait_until(lock,time_point) |
std::cv_status::timeout 或 std::cv_status::no_timeout wait_for(lock,duration,predicate) wait_until(lock,time_point,predicate) |
Bool——被唤醒时断言的返回值 |
std::timed_mutex, std::recursive_timed_mutex 或 std::shared_timed_mutex | try_lock_for(duration) try_lock_until(time_point) |
Bool——若获取了锁,则返回 true,否则返回 false |
std::shared_timed_mutex | try_lock_shared_for(duration) try_lock_shared_until(time_point) |
Bool——若获取了锁,则返回 true,否则返回 false |
std::unique_lock unique_lock(lockable,duration) unique_lock(lockable,time_point) |
try_lock_for(duration) try_lock_until(time_point) |
无——如果在新构建的对象上获取了锁,那么 owns_lock() 返回 true,否则返回 false Bool——若获取了锁,则返回 true,否则返回 false |
std::shared_lock shared_lock(lockable,duration) shared_lock(lockable,time_point) |
try_lock_for(duration) try_lock_until(time_point) |
无——如果在新构建的对象上获取了锁,那么 owns_lock() 返回 true,否则返回 false Bool——若获取了锁,则返回 true,否则返回 false |
std::future或std::shared_future | wait_for(duration) wait_until(time_point) |
如果等待超时则返回 std::future_status::timeout 如果 future 已就绪则返回 std::future_status::ready 如果 future 上的函数按推迟方式执行,且尚未开始执行,则返回 std::future_status::deferred |
一种简化代码的途径是:在并发实战中使用非常贴近函数式编程的风格。线程间不会直接共享数据,而是由各任务分别预先备妥自己所需的数据,并借助 future 将结果发送到其他有需要的线程。
利用 future 进行函数式编程
术语“函数式编程”是指一种编程风格,函数调用的结果完全取决于参数,而不依赖任何外部状态。这源自数学概念中的函数,它的意义是,若我们以相同的参数调用一个函数两次,结果会完全一致。
future 对象可在线程间传递,所以一个计算任务可以依赖另一个任务的结果,却不必显式地访问共享数据。
函数式编程风格的快速排序:
// 快速排序的串行实现
template
std::list sequential_quick_sort(std::list input)
{
if(input.empty())
{
return input;
}
std::list result;
result.splice(result.begin(), input, input.begin());
T const& pivot = *result.begin();
auto divide_point = std::partition(input.begin(), input.end(),
[&](T const& t) { return t lower_part;
lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
auto new_lower(sequential_quick_sort(std::move(lower_part)));
auto new_higher(sequential_quick_sort(std::move(input)));
result.splice(result.end(), new_higher);
result.splice(result.begin(), new_lower);
return result;
}
函数式编程风格的并行快速排序
// 运用 future 实现并行快速排序
template
std::list parallel_quick_sort(std::list input)
{
if(input.empty())
{
return input;
}
std::list result;
result.splice(result.begin(), input, input.begin());
T const& pivot = *result.begin();
auto divide_point = std::partition(input.begin(), input.end(),
[&](T const& t) { return t lower_part;
lower_part.splice(lower_part.end(), input, input.begin(), divide_point);
std::future> new_lower(std::async(¶llel_quick_sort, std::move(lower_part)));
auto new_higher(parallel_quick_sort(std::move(input)));
result.splice(result.end(), new_higher);
result.splice(result.begin(), new_lower.get());
return result;
}
使用消息传递进行同步
以下内容:略