眠りネ*_*ネロク 2 c++ multithreading initialization c++11 std-call-once
我正在阅读《C++ Concurrency in Action, 2nd Edition X》一书。本书包含一个示例,该示例将std::call_once()函数模板与对象一起使用,以线程安全的方式std::once_flag提供某种延迟初始化。
这是本书的简化摘录:
class X {
public:
X(const connection_details& details): connection_details_{details}
{}
void send_data(const data_packet& data) {
std::call_once(connection_init_, &X::open_connection, this);
connection_.send(data); // connection_ is used
}
data_packet receive_data() {
std::call_once(connection_init_, &X::open_connection, this);
return connection_.recv(data); // connection_ is used
}
private:
void open_connection() {
connection_.open(connection_details_); // connection_ is modified
}
connection_details connection_details_;
connection_handle connection_;
std::once_flag connection_init_;
};
Run Code Online (Sandbox Code Playgroud)
上面的代码的作用是延迟连接的创建,直到客户端想要接收数据或有数据要发送为止。连接是由open_connection()私有成员函数创建的,而不是由 的构造函数创建的X。构造函数仅保存连接详细信息,以便能够在以后创建连接。
上面的成员函数只open_connection()被调用一次,到目前为止一切顺利。在单线程上下文中,这将按预期工作。但是,如果多个线程调用同一对象上的send_data()或成员函数怎么办?receive_data()
connection_显然,中数据成员的修改/更新与或open_connection()中的任何使用都不同步。send_data()receive_data()
是否会std::call_once()阻塞第二个线程,直到第一个线程从 中返回std::call_once()?
第3.3.1节。:在初始化期间保护共享数据
基于这篇文章,我创建了这个答案。
我想看看是否与同一对象上std::call_once()的其他调用同步。以下程序创建了多个线程,这些线程调用一个函数,该函数包含一个使调用线程长时间休眠的调用。std::call_once()std::once_flagstd::call_once()
#include <mutex>
std::once_flag init_flag;
std::mutex mtx;
Run Code Online (Sandbox Code Playgroud)
init_flag是std::once_flag与调用一起使用的对象std::call_once()。互斥锁mtx只是为了避免在从不同线程流式std::cout传输字符时出现交错输出std::cout。
该init()函数是由 调用的函数std::call_once()。它显示文本initialising...,使调用线程休眠三秒钟,然后done在返回之前显示文本:
#include <thread>
#include <chrono>
#include <iostream>
void init() {
{
std::lock_guard<std::mutex> lg(mtx);
std::cout << "initialising...";
}
std::this_thread::sleep_for(std::chrono::seconds{3});
{
std::lock_guard<std::mutex> lg(mtx);
std::cout << "done" << '\n';
}
}
Run Code Online (Sandbox Code Playgroud)
该函数的目的是休眠足够长的时间(在本例中为三秒),以便剩余线程有足够的时间来到达调用std::call_once()。这样我们就能够看到它们是否阻塞,直到执行该函数的线程从中返回。
该函数do_work()由在以下位置创建的所有线程调用main():
void do_work() {
std::call_once(init_flag, init);
print_thread_id();
}
Run Code Online (Sandbox Code Playgroud)
init()只会被一个线程调用(即,只会被调用一次)。所有线程都调用print_thread_id(),即,为 中创建的每个线程执行一次main()。
简单print_thread_id()地显示当前线程ID:
void print_thread_id() {
std::lock_guard<std::mutex> lg(mtx);
std::cout << std::this_thread::get_id() << '\n';
}
Run Code Online (Sandbox Code Playgroud)
do_work()总共创建了16 个调用该函数的线程main():
#include <vector>
int main() {
std::vector<std::thread> threads(16);
for (auto& th: threads)
th = std::thread{do_work};
for (auto& th: threads)
th.join();
}
Run Code Online (Sandbox Code Playgroud)
我在系统上得到的输出是:
initialising...done
0x7000054a9000
0x700005738000
0x7000056b5000
0x700005632000
0x700005426000
0x70000552c000
0x7000055af000
0x7000057bb000
0x70000583e000
0x7000058c1000
0x7000059c7000
0x700005a4a000
0x700005944000
0x700005acd000
0x700005b50000
0x700005bd3000
Run Code Online (Sandbox Code Playgroud)
此输出意味着在print_thread_id()第一个调用的线程std::call_once()从该线程返回之前,不会执行任何线程。这意味着这些线程在std::call_once()调用时被阻塞。
| 归档时间: |
|
| 查看次数: |
1828 次 |
| 最近记录: |