Daily bit(e) C++ #33, Низкоуровневый инструмент одиночного вызова: std::call_once
std::call_once
— это низкоуровневый инструмент синхронизации, гарантирующий один успешный (вызов, который не завершается) вызов callable.
Все последующие попытки (даже сделанные во время одиночного вызова) будут синхронизированы с выходом вызываемого объекта, а это означает, что любые изменения, сделанные во время вызова, будут видны всем потокам.
Хотя мы можем имитировать подобное поведение, используя локальную статическую переменную, этот подход работает только для синхронизации одной функции. std::call_once
можно использовать в нескольких функциях.
#include <memory> #include <mutex> #include <thread> #include <vector> #include <algorithm> constexpr inline std::string_view resource_path = "/some/path"; struct GlobalResource { struct Header{}; struct Body{}; static const Header& header() { std::call_once(flag_, load_resource, resource_path); return instance_->header_; } static const Body& body() { std::call_once(flag_, load_resource, resource_path); return instance_->body_; } private: Header header_; Body body_; static void load_resource(std::string_view path) { auto res = std::make_unique<GlobalResource>(); instance_ = std::move(res); } static std::once_flag flag_; static std::unique_ptr<GlobalResource> instance_; }; std::unique_ptr<GlobalResource> GlobalResource::instance_ = nullptr; std::once_flag GlobalResource::flag_; int main() { std::vector<std::jthread> runners; // spawn 4 threads std::generate_n(std::back_inserter(runners), 4,[]{ return std::jthread([]{ auto& header = GlobalResource::header(); // process header auto& body = GlobalResource::body(); // process body }); }); }