网站业务费如何做记账凭证,做外贸网站公司哪家好,品牌建设标题,电商怎么入门在工作中看到了如下代码#xff0c;代码基于 std::thread 封装了一个 Thread 类。Thread 封装了业务开发中常用的接口#xff0c;比如设置调度策略#xff0c;设置优先级#xff0c;设置线程名。如下代码删去了不必要的代码#xff0c;只保留能说明问题的代码。从代码实现…在工作中看到了如下代码代码基于 std::thread 封装了一个 Thread 类。Thread 封装了业务开发中常用的接口比如设置调度策略设置优先级设置线程名。如下代码删去了不必要的代码只保留能说明问题的代码。从代码实现上来看我们看不出什么问题创建一个线程第一个形参是线程的入口函数后边的传参是线程入口函数的参数列表。
class Thread {
public:template class Function, class... ArgsThread(Function f, Args ...args) noexcept: internal_{[func std::forwardFunction(f), args...]() {func(args...);}}{}private:std::thread internal_;
}; Thread 类在大部分使用场景下是没问题的比如下面的使用方式创建了一个线程线程中是一个死循环每隔一秒打印一次 thread running可以正常工作。
#include stdio.h
#include unistd.h
#include memory
#include thread
#include vectorclass Thread {
public:template class Function, class... ArgsThread(Function f, Args ...args) noexcept: internal_{[func std::forwardFunction(f), args...]() {func(args...);}}{}private:std::thread internal_;
};void func() {while (1) {printf(thread running\n);sleep(1);}
}int main() {Thread *t new Thread(func);sleep(100);return 0;
} 1 问题现象
在下边这个使用场景下就能暴露出来 Thread 的问题。
如下代码中连续创建了 8 个线程线程的入口函数是 func()func() 的形参是 Obj 对象Obj 中的成员 i_ 分别取值 0 ~ 7。
#include stdio.h
#include unistd.h
#include iostream
#include memory
#include thread
#include vectorclass Thread {
public:template class Function, class... ArgsThread(Function f, Args ...args) noexcept: internal_{[func std::forwardFunction(f), args...]() {func(args...);}}{}private:std::thread internal_;
};class Obj {
public:Obj(int i) {i_ i;std::cout Obj(), i: i_ std::endl;}Obj(const Obj obj) {i_ obj.i_;std::cout copy constructor, i: i_ std::endl;}~Obj() {std::cout ~Obj(), i: i_ std::endl;}int i_;};void func(Obj obj) {printf( in thread, i: %d\n, obj.i_);
}int main() {std::vectorThread * threads;int i 0;for (i 0; i 8; i) {printf( out thread, i: %d\n, i);Obj obj(i);auto tmp new Thread(func, obj);printf(after create thread %d\n, i);threads.emplace_back(tmp);// sleep(2);}sleep(100);return 0;
}上边的代码编译之后运行结果如下所示。我们的预期是在 func() 中的打印分别是 0 ~ 7每个数字打印一次。但实际的打印结果是有重复的如下图所示2 有重复的7 也有重复的。
rootwangyanlong-virtual-machine:/home/wyl/cpp# ./a.outout thread, i: 0
Obj(), i: 0
after create thread 0
~Obj(), i: 0out thread, i: 1
Obj(), i: 1
after create thread 1
~Obj(), i: 1out thread, i: 2
Obj(), i: 2
copy constructor, i: 2in thread, i: 2
~Obj(), i: 2
copy constructor, i: 2in thread, i: 2
~Obj(), i: 2
after create thread 2
~Obj(), i: 2out thread, i: 3
Obj(), i: 3
copy constructor, i: 2in thread, i: 2
~Obj(), i: 2
copy constructor, i: 3in thread, i: 3
~Obj(), i: 3
after create thread 3
~Obj(), i: 3out thread, i: 4
Obj(), i: 4
after create thread 4
~Obj(), i: 4out thread, i: 5
Obj(), i: 5
copy constructor, i: 4
after create thread 5
~Obj(), i: 5out thread, i: 6
Obj(), i: 6in thread, i: 4
~Obj(), i: 4
copy constructor, i: 5in thread, i: 5
~Obj(), i: 5
after create thread 6
~Obj(), i: 6out thread, i: 7
Obj(), i: 7
copy constructor, i: 7in thread, i: 7
~Obj(), i: 7
after create thread 7
~Obj(), i: 7
copy constructor, i: 7in thread, i: 7
~Obj(), i: 7上边的代码把 sleep(2) 注释打开打印结果是符合预期的。 或者将 main() 中的 Thread() 改成 std::thread打印结果也是符合预期的说明这种使用方式是符合 c 规范的。 2 问题分析
导致问题的原因有以下几个方面
1线程的构造函数入参是右值引用这个右值引用的生命周期在构造函数返回的时候已经结束了。右值引用指向一个临时的存储空间在反复创建 8 个线程期间8 个右值引用指向的是同一块内存空间后边的值会将前边的值覆盖。
2线程构造函数中std::thread 的回调函数是一个 lambda 表达式lambda 表达式中引用捕获了 args。
3在 Thread 构造函数中创建了线程但是线程并不是立即执行的从创建到真正执行是有一段时间的延迟。这样当线程真正运行的时候再从 args 引用里边读取数据取出来的是这块内存最新的数据属于这个线程的数据已经被覆盖。 3 问题修改
引用捕获改成值捕获
如下代码在 Thread() 构造函数中的 lambda 表达式对 args 的引用捕获改成值捕获。
#include stdio.h
#include unistd.h
#include iostream
#include memory
#include thread
#include vectorclass Thread {
public:template class Function, class... ArgsThread(Function f, Args ...args) noexcept: internal_{[func std::forwardFunction(f), args...]() {func(args...);}}{}private:std::thread internal_;
};class Obj {
public:Obj(int i) {i_ i;std::cout Obj(), i: i_ std::endl;}Obj(const Obj obj) {i_ obj.i_;std::cout copy constructor, i: i_ std::endl;}~Obj() {std::cout ~Obj(), i: i_ std::endl;}int i_;};void func(Obj obj) {printf( in thread, i: %d\n, obj.i_);
}int main() {std::vectorThread * threads;int i 0;for (i 0; i 8; i) {printf( out thread, i: %d\n, i);Obj obj(i);auto tmp new Thread(func, obj);printf(after create thread %d\n, i);threads.emplace_back(tmp);// sleep(2);}sleep(100);return 0;
}