文章目录
编译选项 cmake/make
std::thread 的实现背后是基于 pthread 的,为了编译通过建议:
Cmake
cmake_minimum_required(VERSION 3.10)
set(CMAKE_CXX_STANDARD 17)
project(cpptest LANGUAGES CXX)
add_executable(cpptest main.cpp)
# 重点是这一步,链接pthread库
target_link_libraries(cpptest PUBLIC pthread)
makefile
excute=out
obj=test.cpp
$(excute): $(obj)
# 重点是 -lpthread
gcc $(obj) -lpthread -lstdc++ -o $(excute)
clean:
rm $(excute)
常用操作
std::this_thread
线程控制自己的方法,在<thread>
头文件中,不仅有std::thread这个类,而且还有一个std::this_thread命名空间,它可以很方便地让线程对自己进行控制。
std::this_thread
是个命名空间,所以你可以使用using namespace std::this_thread;
这样的语句来展开这个命名空间,不过我不建议这么做。
等待 ::sleep_for ::sleep_until
#include <thread>
// 等待到sleep_time时间点
template<class Clock, class Duration >
void sleep_until(const std::chrono::time_point<Clock, Duration>& sleep_time );
// 等待sleep_duration时间段
template<class Rep, class Period >
void sleep_for(const std::chrono::duration<Rep, Period>& sleep_duration );
#include <chrono>
#include <iostream>
#include <thread>
using std::chrono::operator""ms;
std::cout << "Hello, waiter...\n" << std::flush;
const auto start = std::chrono::steady_clock::now();
std::this_thread::sleep_until(start + 2000ms;);
std::chrono::duration<double, std::milli> elapsed {now() - start};
std::cout << "Waiteded until" << elapsed.count() << " ms\n";
using namespace std::chrono_literals;
std::cout << "Hello waiter\n" << std::flush;
const auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(2000ms);
const auto end = std::chrono::high_resolution_clock::now();
const std::chrono::duration<double, std::milli> elapsed = end - start;
std::cout << "Waited " << elapsed << '\n';
挂起 ::yield
挂起当前线程,允许其他线程运行。
依赖于操作系统调度器的机制和系统的状态,例如:先进先出实时调度程序(Linux中的SCHED_FIFO)挂起当前线程,并将其放在准备运行的相同优先级线程的队列后面,如果没有其他具有相同优先级的线程,yield将不起作用。
#include <thread>
// 暂时放弃线程的执行,将主动权交给其他线程
void yield() noexcept;
#include <chrono>
#include <iostream>
#include <thread>
// "busy sleep" while suggesting that other threads run
// for a small amount of time
void little_sleep(std::chrono::microseconds us)
{
auto start = std::chrono::high_resolution_clock::now();
auto end = start + us;
do {
// 暂时挂起
std::this_thread::yield();
} while (std::chrono::high_resolution_clock::now() < end);
}
int main() {
auto start = std::chrono::high_resolution_clock::now();
std::this_thread::sleep_for(std::chrono::microseconds(100));
auto elapsed = std::chrono::high_resolution_clock::now() - start;
std::cout << "waited for "
<< std::chrono::duration_cast<std::chrono::microseconds>(elapsed).count()
<< " microseconds\n";
}
获取id ::get_id
#include <thread>
// 获取当前线程id
std::thread::id get_id() noexcept;
#include <chrono>
#include <iostream>
#include <syncstream>
#include <thread>
using namespace std::chrono_literals;
void foo() {
std::thread::id this_id = std::this_thread::get_id();
std::cout << "thread " << this_id << " sleeping...\n";
std::this_thread::sleep_for(500ms);
}
int main()
{
std::jthread t1{foo};
std::jthread t2{foo};
}
执行线程
在C中已经有一个叫做pthread的东西来进行多线程编程,但是并不好用 (如果你认为句柄、回调式编程很实用,那请当我没说),所以c++11标准库中出现了一个叫作std::thread的东西。
C++中有多种可调用对象,他们可以作为参数传给std::bind()
,std::thread()
, std::async()
,std::call_once()
等。
std::thread 立即执行
是什么
C++11 开始,为多线程提供了语言级别的支持。用 std::thread 这个类来表示线程。作为一个 C++ 类,std::thread 同样遵循 RAII 思想和三五法则。
从概念上来讲,同一个线程不允许两个 std::thread
对象管理。所以
-
删除 拷贝 构造/赋值函数,
-
提供 移动 构造/赋值函数。
-
自定义了 析构函数
当
std::thread对象
离开作用域(例如所在的函数退出时),std::thread
的解构函数会销毁线程。可能导致std::thread
对象的函数还没开始执行,线程就被销毁了。
返回值:可通过std::promise
或 修改共享变量(请结合std::mutex
和std::atomic
)。被执行函数的返回值默认被忽略。(见下一大章节)
异常:建议抛出 std::terminate
// 默认构造函数
// 创建std::thread对象,但什么也不做
thread() noexcept;
// 初始化构造函数
// 创建std::thread对象,以args为参数,开始执行fn函数(注意,开始执行)
template <class Fn, class… Args>
explicit thread(Fn&& fn, Args&&… args);
// 移动构造函数
thread(thread&& x) noexcept;
// 移动拷贝
thread& operator=(thread &&rhs);
// 禁止复制拷贝
thread( const thread& ) = delete;
// 析构函数 析构对象
~thread();
执行线程(默认)
std::thread管理的线程,何时执行与回收?
-
执行:线程是在
std::thread
对象被定义时开始执行。 -
回收:
std::thread
对象析构时,必须执行过一次 join函数 或 detach函数,否则报错执行std::terminate
。(哪怕函数确实完整执行完了)- join函数——阻塞等待线程结束并回收资源。
- detach —— 进程执行结束时,该线程哪怕没执行完也会强制关闭。(不推荐),可能因资源未释放而导致内存泄漏。
多次调用 join 或 detach 会abort 哦。
// 初始化构造
std::thread t1([&] { // 开始执行
std::this_thread::sleep_for(std::chrono::microseconds(100));
});
t1.detach(); // t1.join();
if (t1.joinable()) { t1.join(); }
// 默认构造
std::thread t2;
std::cout << "before starting" << std::endl;
t2 = std::thread{foo}; // 开始执行
std::cout << "after starting" << std::endl;
t2.join();
问题:那如果我寻思,反正是默认执行,只要执行完了是不是就得了?我不阻塞等待或放弃管理行不行?
答案:不行!放弃管理也需要显示声明你放弃了管理!例如:
{
std::thread t1 {[] {
std::cout << "lalal \n";
std::this_thread::sleep_for(std::chrono::seconds(1));
std::cout << "done \n";
}
};
// t1.join();
std::this_thread::sleep_for(std::chrono::seconds(3));
}
std::cout << "exit {} \n";
明明线程已经顺利执行结束了,但是因为 std::thread 析构之前,没有执行 join 或 detach 所以存在报错,输出如下:
lalal
done
terminate called without an active exception
Aborted
等待、回收
前文讲过,没有执行join或detach的std::thread会在析构时会引发异常。
阻塞等待 join
waits for the thread to finish its execution
阻塞当前线程,直到 std::thread
对象管理的其*this
标识的线程完成执行。
换句人话:让父线程不要急着退出,等子线程也结束了再运行。
- 子线程不是在调用join函数时才执行的
- 主线程阻塞等待子线程结束并回收资源
// 等待线程结束并清理资源(会阻塞)
void join();
// 返回线程是否可以执行join函数
bool joinable() const noexcept;
std::thread t1([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
});
t1.detach();
if (t1.joinable()) {
t1.join();
}
放弃管理 detach
permits the thread to execute independently from the thread handle
void detach();
std::thread::detach —— 分离 std::thread对象 和 将在执行的线程,线程继续独立执行。
意味着线程的生命周期不再由当前 std::thread 对象管理
std::thread
对象的*this
不再拥有该线程。- 执行过detach 而 分离的线程,像pthread执行detach后一样,在调用它的线程结束或自己结束时释放资源。
- 线程正常结束,在函数运行完毕后自动释放,自动销毁自己。
- 强制结束线程(不推荐),可能因资源未释放而导致内存泄漏 —— 进程退出,自动退出。
std::thread t1([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
});
t1.detach();
返回值 std::promise
作用
一般情况下:
- 使用
std::promise
获取thread创建线程的返回值。 - 使用
std::future
获取async创建线程的返回值。
std::promise
实际上是std::future
的一个包装。如果使用thread以引用传递返回值的话,就必须要改变future的值,std::future的值不能改变,那么如何利用引用传递返回值?
答:改不了 我新建一个
-
可以通过promfise来创建一个拥有特定值的future。
返回的是新创建的future对象,没有改变任何已有future的值
-
在外面看起来就是,future的值不能改变,promise的值可以改变。
使用方法
如何获得thread的返回值?过程是什么样的呢?
-
在主线程
-
声明一个
std::promise<T>
变量,其T类型与返回值相同。 -
抽象出来的意义是:这个值,虽然现在还没有,但是以后会有的,会有子线程把它填上的。
-
-
在子线程中,
-
捕获刚刚设置的
std::promise<T>
变量。(函数入参/lambda捕获) -
向
std::promise<T>
设置在上一步承诺过会被填进去的值。即,调用
std::promise
的set_value()
方法。
-
-
在主线程里,
-
用
get_future()
获取 std::thread 的 std::future 对象, -
进一步
std::future::get()
可以等待并获取线程返回值。
-
如果线程没有执行完,这里就阻塞。对应的场景就是,哎,你不是说过以后会有返回值的吗?你不能烂尾,就阻塞在这里,弄完了返回值再走。
void download(std::string file, promise<string> &pret) {
std::this_thread::sleep_for(std::chrono::milliseconds(400));
pret.set_value("for promise"); // 向promise中写入值,创建future
// return 1;
}
int main() {
//声明一个std::promise对象pret,其保存的值类型为int
std::promise<string> pret;
std::thread t1([&] {
download("hello.zip", pret);
});
std::future<string> fret = pret.get_future(); // 从promise中取出future
string ret = fret.get(); // 从future中取出返回值
std::cout << "Download result: " << ret << std::endl;
t1.join();
return 0;
}
常用函数
// 默认构造函数 构造一个空的promise对象
promise();
// 带参构造函数
// 与默认构造函数相同,但使用特定的内存分配器alloc构造对象
template <class Alloc>
promise(allocator_arg_t aa, const Alloc& alloc);
// 复制构造函数(已删除)
promise (const promise&) = delete;
// 移动构造函数
promise (promise&& x) noexcept;
// 析构函数
~promise();
set_value
std::promise<void>
的 set_value() 不接受参数,仅仅作为同步用,不传递任何实际的值。
// set_value函数
// - 设置promise的值
// - 将共享状态设为ready(将future_status设为ready)
// void特化:只将共享状态设为ready
void set_value (const T& val)
void set_value (T&& val)
// 偏特化: 引用
void promise<R&>::set_value (R& val)
// 偏特化: void
void promise::set_value (void)
get_future
// 构造一个future对象,
// 其值与promise相同,status也与promise相同
future get_future()
demo
声明保存 int 的promise
void download(std::string file, promise<int> &pret) {
std::this_thread::sleep_for(std::chrono::milliseconds(400));
pret.set_value(404); // 向promise中写入值,创建future
}
int main() {
//声明一个std::promise对象pret,其保存的值类型为int
std::promise<int> pret;
std::thread t1([&] {
auto ret = download("hello.zip", pret);
});
std::future<int> fret = pret.get_future(); // 从promise中取出future
int ret = fret.get(); // 从future中取出返回值
std::cout << "Download result: " << ret << std::endl;
t1.join();
return 0;
}
全局线程池
detach 的问题是进程退出时,进程不会等待所有子线程执行完毕。
另一种解法是把 t1 对象移动到一个全局变量去,再join它们,从而延长其生命周期到 myfunc 函数体外。
vector
从中取出thread,手动join
std::vector<std::thread> pool;
void myfunc() {
std::thread t1([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
});
pool.push_back(std::move(t1)); // 移交thread的控制权(所有权)给全局变量,延长生命周期
}
void main() {
myfunc();
for (auto &t: pool) t.join(); // 手动join
}
线程池
在 main 里面手动 join 全部线程还是有点麻烦,
我们可以自定义一个类 ThreadPool,并用他创建一个全局变量,其解构函数会在 main 退出后自动调用。
class ThreadPool {
std::vector<std::thread> m_pool;
public:
void push_back(std::thread thr) {
m_pool.push_back(std::move(thr));
}
~ThreadPool() { // 即将离开作用域(进程要结束时),自动调用
for (auto &t: m_pool) t.join(); // 等待线程池中的线程全部结束
}
};
ThreadPool tpool;
void myfunc() {
std::thread t1([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
});
tpool.push_back(std::move(t1)); // 移交thread的控制权(所有权)给全局变量,延长生命周期
}
int main() {
myfunc();
return 0;
}
std::async 延迟执行(可选同步异步)
概念
出现原因:
-
可以选择执行方式:
-
手动选择 同步执行(等同于普通的函数调用)
-
手动选择 异步执行
函数的执行仍在主线程中,只是函数式编程范式意义上的异步,而不涉及到真正的多线程。
-
默认 交给操作系统选择
-
-
返回值操作更加方便
- std::async——std::future
- std::thread——std::promise
不同于 std::thread,std::async是一个模板函数,所以没有成员函数,有两种构造函数:
// 异步/同步交给操作系统选择
// - 入参:带返回值的lambda及其参数
// - 返回值: std::future 对象。
template <class Fn, class… Args>
future<typename result_of<Fn(Args…)>::type>
async (Fn&& fn, Args&&… args);
// 异步/同步手动指定选择
// - 入参:手动选择的policy、带返回值的lambda及其参数
// policy :
// 同步执行 launch::deferred
// 异步执行,创建新线程 launch::async
// - 返回值: std::future 对象。
template <class Fn, class… Args>
future<typename result_of<Fn(Args…)>::type>
async (launch policy, Fn&& fn, Args&&… args);
选择执行方式
默认(不指定)
视操作系统而定。
// 不显式指定,交给操作系统
std::future<int> fret = std::async([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
int ret = fret.get();
// 不指定,显式的交给操作系统
std::future<int> fret = std::async(
std::launch::async | std::launch::defereed,
[&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
}
);
int ret = fret.get();
指定执行方式
异步或同步,根据policy参数而定。std::launch
有2个枚举值和1个特殊值:
template <class Fn, class… Args>
future<typename result_of<Fn(Args…)>::type>
async (launch policy, Fn&& fn, Args&&… args);
同步(无子线程) ::deferred
std::launch::deferred
也就是 0x2
-
同步启动
-
创建
std::async
时,不会创建一个线程来执行, -
在调用
future::get
、future::wait
时,才启动函数体推迟 lambda 函数体内的运算 到 future 的 get()或 wait() 被调用时
-
-
函数的执行仍在主线程中
这只是函数式编程范式意义上的异步,而不涉及到真正的多线程。
-
可以用这个实现惰性求值(lazy evaluation)之类。
std::future<int> fret = std::async(std::launch::deferred, [&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
// 此时才开始启动
int ret = fret.get();
异步 ::async
std::launch::async
也就是 0x1
异步启动——创建async时,创建一个线程来执行,
// 此时就已经启动
std::future<int> fret = std::async(std::launch::async, [&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
int ret = fret.get();
执行线程/返回值 std::future
std::async
返回值为 std::future
,通过 std::future
可以开始让线程执行。
std::future的作用,作为句柄:
- 获取 async 返回值
- 让 async 阻塞等待/限时等待
- 检测 async 线程是否已结束
所以对于返回值是void的线程来说,future也同样重要。
std::async
里 lambda 的返回类型可以为 void, 这时 future 对象的类型为std::future<void>
。
int main() {
future<int> val = async(launch::async, sum<int, int, int>, 1, 10, 100);
// future::get() 阻塞等待线程结束并获得返回值
cout << val.get() << endl;
return 0;
}
// 默认构造函数
// 构造一个空的、无效的future对象,但可以移动分配到另一个future对象
future() noexcept;
// 复制构造函数 (已删除)
future(const future&) = delete;
// 移动构造函数 构造一个与x相同的对象并破坏x
future (future&& x) noexcept;
// 析构函数
~future();
常用方法
需要调用 future 的方法,等待线程执行完毕。
get()
调用次数:只能调用一次
行为:
-
阻塞等待线程结束并获取返回值
-
如果还没完成,阻塞等待,
只要线程没有执行完,会无限等下去。
-
获取返回值。
-
-
若future类型为void,则
future::get()
与future::wait()
相同 -
如果是同步
launch::deferred
的async,启动的asycn函数
T get() // 一般情况
R& future<R&>::get() // 类型为引用
void:void future::get() // 当类型为void,则与future::wait()相同
std::future<int> fret = std::async([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
// 等待线程执行结束
int ret = fret.get();
wait()
调用次数:只能调用一次
行为:
-
阻塞等待线程结束
如果还没完成,阻塞等待,只要线程没有执行完,会无限等下去。
-
不会获取返回值。
但是可以在
future::wait()
结束后,再次通过future::get()
获取返回值 -
如果是同步
launch::deferred
的async,启动的asycn函数
std::future<int> fret = std::async([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
// 等待线程执行结束,不获取返回值
fret.wait();
// 虽然已经结束,但还是可以获取返回值
int ret = fret.get();
wait_for() wait_until()
wait_for()
调用次数:无限制
行为:
-
如果还没完成,阻塞等待,直到线程结束/限定时间到
- 可以用 chrono 里的类表示单位,指定一个最长等待时间。
- 线程结束/限定时间到 结束阻塞
-
返回值类型:
std::future_status
表示等待是否成功。-
返回
std::future_status::timeout
线程在指定的时间内没有执行完毕,放弃等待
-
返回
std::future_status::ready
线程在指定的时间内执行完毕,等待成功
-
-
如果是同步
launch::deferred
的async,启动的asycn函数
template <class Rep, class Period>
future_status wait_for(const chrono::duration<Rep,Period>& rel_time) const;
std::future<int> fret = std::async([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
while (true) {
// 循环多次等待
auto stat = fret.wait_for(std::chrono::milliseconds(1000));
if (stat == std::future_status::ready) {
std::cout << "Future is ready!!" << std::endl;
break;
} else {
std::cout << "Future not ready!!" << std::endl;
}
}
// 虽然已经结束,但还是可以获取返回值
int ret = fret.get();
wait_until()
同理 wait_for() ,其参数是一个时间点。
std::future<int> fret = std::async([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
return 1;
});
while (true) {
// 循环多次等待
auto stat = fret.wait_for(std::chrono::milliseconds(1000));
if (stat == std::future_status::ready) {
std::cout << "Future is ready!!" << std::endl;
break;
} else {
std::cout << "Future not ready!!" << std::endl;
}
}
// 虽然已经结束,但还是可以获取返回值
int ret = fret.get();
std::shared_future
std::future 为了满足三五法则,删除了拷贝构造/赋值函数。如果需要浅拷贝,实现共享同一个 future 对象,可以用 std::shared_future。
void download(std::string file) {
std::this_thread::sleep_for(std::chrono::milliseconds(400));
std::cout << "Download complete: " << file << std::endl;
}
int main() {
std::shared_future<void> fret = std::async([&] {
std::this_thread::sleep_for(std::chrono::microseconds(100));
});
auto fret2 = fret;
auto fret3 = fret;
fret3.wait();
std::cout << "Download completed" << std::endl;
return 0;
}
示例
#include <algorithm>
#include <future>
#include <iostream>
#include <mutex>
#include <numeric>
#include <string>
#include <vector>
std::mutex m;
struct X
{
void foo(int i, const std::string& str)
{
std::lock_guard<std::mutex> lk(m);
std::cout << str << ' ' << i << '\n';
}
void bar(const std::string& str)
{
std::lock_guard<std::mutex> lk(m);
std::cout << str << '\n';
}
int operator()(int i)
{
std::lock_guard<std::mutex> lk(m);
std::cout << i << '\n';
return i + 10;
}
};
template<typename RandomIt>
int parallel_sum(RandomIt beg, RandomIt end)
{
auto len = end - beg;
if (len < 1000)
return std::accumulate(beg, end, 0);
RandomIt mid = beg + len / 2;
auto handle = std::async(std::launch::async,
parallel_sum<RandomIt>, mid, end);
int sum = parallel_sum(beg, mid);
return sum + handle.get();
}
int main()
{
std::vector<int> v(10000, 1);
std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n';
X x;
// Calls (&x)->foo(42, "Hello") with default policy:
// may print "Hello 42" concurrently or defer execution
auto a1 = std::async(&X::foo, &x, 42, "Hello");
// Calls x.bar("world!") with deferred policy
// prints "world!" when a2.get() or a2.wait() is called
auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!");
// Calls X()(43); with async policy
// prints "43" concurrently
auto a3 = std::async(std::launch::async, X(), 43);
a2.wait(); // prints "world!"
std::cout << a3.get() << '\n'; // prints "53"
} // if a1 is not done at this point, destructor of a1 prints "Hello 42" here
The sum is 10000
43
world!
53
Hello 42
std::packaged_task 延迟异步执行
std::packaged_task - cppreference.com
template< class R, class ...ArgTypes >
class packaged_task<R(ArgTypes...)>;
std::packaged_task<F>
,绑定任何可调用对象(函数、lambda表达式、绑定表达式或其他函数对象),可获取future句柄,以便可以在未来进行异步调用。
封装任何Callable目标,它的返回值或抛出的异常存储在共享状态中,可以通过std::future对象访问。
-
更方便的,天然异步的函数对象(async除非显式声明,否则不一定异步执行)。
-
返回值:需要将可调对象传递给关联的
std::future
对象(通过get_future成员函数)-
包装
std::promise<T= std::function<F>>
中的可调对象T= std::function<F>
-
可调用对象的执行、返回值获取,需要透过
future
。
-
int download02() {
cout << "start download " << endl;
std::this_thread::sleep_for(std::chrono::milliseconds(400));
cout << "set promise value " << endl;
return 2;
}
int main()
{
// 给packaged_task绑定函数对象
std::packaged_task<int()> t(download02);
// 指定函数对象的future返回值
std::future<int> fu2 = t.get_future();
// 执行packaged_task绑定的函数对象
t();
// 先执行之后,才可以获取返回值
int result = fu2.get();
std::cout << result << std::endl;
return 0;
}
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';
int f(int x, int y) { return std::pow(x, y); }
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';
考虑这样一个场景:多个线程共享一个任务队列
- 一个线程负责生产任务
- 将任务放到任务队列中
- 在这个任务执行后获取它的返回值.
- 多个子线程从任务队列中取出任务并执行
这里简化一下这个场景。主线程产生任务,一个子线程t1
执行。
std::invoke_result_t
std::packaged_task
std::jthread 自动join
C++20 引入了 std::jthread 类,
和 std::thread 不同在于:
- 他的解构函数里会自动调用 join() 函数,
- 保证了解构时会自动等待线程执行完毕。
native_handler
jthread 自动join
std::call_once 执行一次
在多线程的环境下, 某些可执行对象只需要执行一次。std::call_once 应运而生。
- 封装了加锁和修改flag的过程。简化代码(不再需要判空/添加flag),增强可读性。
- 本身不具有类似 thread.join() 的功能。需要子线程join 或 主线程进行等待
原理:判断全局变量标识符once_flag。如果这个once_flag
- “未执行过”
- 加锁,执行,结束后释放锁mutex
- 修改标识符为**“执行过”**
- “执行过” 被标记过。不再执行。
template< class Function, class... Args >
void call_once (std::once_flag& flag, Function&& f, Args&& args... );
// 参数解析:
// std::once_flag flag 判断是个需要执行。若执行,执行后关闭。
// f 和 args... 需要被调用的函数以及函数f的传参
// lambda 常用 [this]
// 抛出异常 std::system_error if any condition prevents calls to call_once from executing as specified any exception thrown by f
// 初始化资源(节省数组判空)
// 单例模式
#include <functional>
#include <iostream>
#include <thread>
#include <mutex>
std::once_flag flag1;
class PySingleton {
public:
static PySingleton* mInstance;
static PySingleton* get_instance() {
std::call_once(flag1, [] (int id) {
mInstance = new PySingleton(id);
}, 1);
return mInstance;
}
private:
PySingleton(int id) {
std::cout << " do init, id = " << id << " \n";
}
};
PySingleton* PySingleton::mInstance = nullptr; // 必须在这里 为static mInstance,编译期执行
void test() {
PySingleton* py = PySingleton::get_instance();
}
int main()
{
std::thread mThread(test);
mThread.join();
return 0;
}
#include <functional>
#include <iostream>
#include <thread>
#include <mutex>
std::once_flag flag1;
void do_print1() {
std::cout << "do_print1 example: called once\n";
}
void do_print2() {
std::cout << "do_print2 example: called once\n";
}
void simple_do_once()
{
std::function<void()> mFunc1 = do_print1;
std::function<void()> mFunc2 = do_print2;
std::call_once(flag1, mFunc1); // 仅仅执行了一次这个
std::call_once(flag1, mFunc2);
}
int main()
{
std::thread st1(simple_do_once);
std::thread st2(simple_do_once); // 什么都没做
st1.join();
st2.join();
return 0;
}