最近在捣鼓C++的多线程编程、在C++20的标准库中新增了标准线程库thread
,这个库非常好用、可以为函数指针
、具有括号运算符成员函数的结构体或对象
、甚至lambda表达式
创建运行它们的线程。
如果使用C++20之前的C++版本、是没有thread
这个线程库的,如果在单片机上使用C++的话标准线程库可能也得不到很好的支持
目前armclang只支持到c++17 用它连标准线程库都没有
我自己写了一个用在C++的线程库,它提供类似std::thread
的功能;它实际上是个中间件、需要系统支持(windows有CreateThread,单片机有各种RTOS)
这个中间件能像
std::thread
那样启动运行lambda的线程,这个特性能极大方便在C++中编写涉及多线程的内容。
问题
std::thread
需要在构造函数中传入可调用对象,我的线程库也应该可以这样,但是在这出现了问题。
我的线程库
这里放出简化的代码、以便于后续的讨论:
namespace H{
class Thread_LL_Abstract;//线程低层抽象
//线程类
template<class Thread_LL>
class Thread{
public:
template<typename T_Invoke,typename... T_Args>
Thread(T_Invoke&& invoke,T_Args&&... args){
Construction(static_cast<T_Invoke&&>(invoke),static_cast<T_Args&&>(args)...);
}
};
class Thread_LL_std:public Thread_LL_Abstract;//继承了线程低层抽象,不同的系统上有不同的实现;
//Thread_LL_std在windows上封装了std::thread或者CreateThread 单片机上可以封装FreeRTOS的相关函数
using Thread_std=Thread<Thread_LL_std>;//线程类模板特化,后续使用Thread_std来创建线程
}
线程类Thread是一个模板、其构造函数也是一个模板。
出现问题的代码
我先后在mingw64 (v13.2.0)与armclang6.19进行测试、结果一致。
std::string s="123";
//下面的代码出现了问题
H::Thread_std thread=H::Thread_std([](std::string& s) {
std::cout<<"Thread Run"<<s<<std::endl;
s="456";
},std::ref(s));
thread.Start();
thread.Join();
std::cout<<s<<std::endl;
这段代码在C++17与C++20中是正常的,但在C++11与C++14则发生了错误:
可以发现、编译器将模板形参T_Invoke
识别为了H::Thread<H::Thread_LL_std>
(即Thread_std
)。但是代码里要创建的对象就是Thread_std
、根本没有为模板类型列表提供Thread_std
这个类型,这里正确的行为应该是T_Invoke=lambda [](std::string &s)->void
(或者与lambda相关的类型、反正不应该是Thread_std
)
如果不是在C++20下编译发现正常, 就真的以为是自己代码的问题了。
我后续使用一个更简单的例子尝试复现这个错误、但是失败了,这个错误的触发条件不明确。
解决方法
将出问题的代码改成:
H::Thread_std thread([](std::string& s) {
std::cout<<"Thread Run"<<s<<std::endl;
s="456";
},std::ref(s));
也就是换了一种声明/构造格式,居然能编译通过了,而且运行正常。
通过new的方式也是没问题的:
H::Thread_std* pthread=new H::Thread_std([](std::string& s) {
std::cout<<"Thread Run"<<s<<std::endl;
s="456";
},std::ref(s));
出问题的就只有H::Thread_std thread=H::Thread_std()
这种构造形式的代码。
问题也算是有了一个解决方法,可以愉快的在类内创建调用成员函数的线程了