1. std::thread类
/*
源码来自vs2017
*/using _Thrd_id_t = unsigned int;
struct _Thrd_t { // thread identifier for Win32void* _Hnd; // Win32 HANDLE_Thrd_id_t _Id; //保存线程id
};class thread { // class for observing and managing threads
public:class id;using native_handle_type = void*;thread() noexcept : _Thr{} {}private:...template <class _Fn, class... _Args>void _Start(_Fn&& _Fx, _Args&&... _Ax) {...}public:template <class _Fn, class... _Args, enable_if_t<!is_same_v<_Remove_cvref_t<_Fn>, thread>, int> = 0>_NODISCARD_CTOR explicit thread(_Fn&& _Fx, _Args&&... _Ax) {_Start(_STD forward<_Fn>(_Fx), _STD forward<_Args>(_Ax)...);}thread(thread&& _Other) noexcept : _Thr(_STD exchange(_Other._Thr, {})) {}thread& operator=(thread&& _Other) noexcept {...}thread(const thread&) = delete;thread& operator=(const thread&) = delete;~thread() noexcept {...}void swap(thread& _Other) noexcept {...}//判断线程是否可以被join,实际判断线程ID是否为0_NODISCARD bool joinable() const noexcept {...}//等待线程void join() {...}//线程分离void detach() {...}//获取线程ID_NODISCARD id get_id() const noexcept;//获取win32句柄_NODISCARD native_handle_type native_handle() noexcept /* strengthened */ { // return Win32 HANDLE as void *return _Thr._Hnd;}//检测硬件并发特性_NODISCARD static unsigned int hardware_concurrency() noexcept {return _Thrd_hardware_concurrency();}private:_Thrd_t _Thr;
};
常用的接口有: 构造函数, joinable(), join(),detach(),get_id();
2. std::thread 无参使用
void threadFunc() { std::this_thread::sleep_for(std::chrono::seconds(5));std::cout << "thread ID: " << std::this_thread::get_id() << std::endl;
}int main()
{std::thread myThread(threadFunc);cout << myThread.get_id() << endl;if (myThread.joinable()) {//myThread.detach(); 线程分离,只能看到 main ID 打印myThread.join(); //都能看见ID打印}return 0;
}
3. std::thread参数传递(避坑)
当线程传递参数时,需特别注意与detech()一起使用时出现的问题;
3.1 传递临时对象作为线程参数
void threadFunc1(const int& i, char* buf)
{std::cout << "&i=" << &i << " &buf=" << (void *)buf << std::endl;&i = 0000023590C30A98 & buf = 00000037DD95FA58
}int main()
{int val = 10;int &reVal = val;char buf[] = "this is test";std::thread myThread(threadFunc1, val,buf);cout << "main &val=" << &val << " &buf=" << &buf << endl;//main &val=00000037DD95FA14 &buf=00000037DD95FA58if (myThread.joinable()) {//myThread.join(); //没啥问题myThread.detach(); }return 0;
}
当使用引用和指针作为线程参数传递时, 经过打印知 i实际为值传递, 而buf与main地址相同;
那么就可能存在detach()时,主线程已经跑完且buf地址已经失效,而线程中可能还在使用该地址;
避坑:
// const string& buf建议使用引用,省一次构造
void threadFunc2(const int i, const string& buf)
{std::cout << i << " "<< buf.c_str() << std::endl;
}int main()
{int val = 10;int &reVal = val;char buf[] = "this is test";//直接将其转为string对象,且要保证在main中就转成功;//即使这里是引用, 也能够保证两者地址不一样;std::thread myThread(threadFunc2, val,string(buf));if (myThread.joinable()) {//myThread.join(); //没啥问题myThread.detach(); }return 0;
}
3.2 传递类对象作为线程参数
//不加&多产生一次拷贝构造,建议都加上;
void threadFunc3(const A &buf)
{std::cout << buf.m_i << endl;
}int main()
{A objA(10);std::thread myThread(threadFunc3, objA);//就想传递引用呢, 加上std::ref即可//std::thread myThread(threadFunc3, std::ref(objA));if (myThread.joinable()) {myThread.join(); }return 0;
}
3.3 传递智能指针作为线程参数
void threadFunc4(std::unique_ptr<int> ptr)
{
}int main()
{std::unique_ptr<int> myPtr(new int(10));//传递后myPtr指向空了 不能使用detech,因为主线程先执行完,myPtr指向的对象被释放了;std::thread myThread(threadFunc4, std::move(myPtr)); // 移动智能指针到线程if (myThread.joinable()) {myThread.join(); //myThread.detach();//一定不能是detach}return 0;
}
3.4 传递成员函数指针作为线程参数
class A
{
public:int m_i;//类型转换构造函数,可以把一个Int转成类对象;A(int a) :m_i(a) { cout << "构造" << this << "thread id= " << std::this_thread::get_id() << endl; }A(const A& a) :m_i(a.m_i) { cout << this << "thread id= " << std::this_thread::get_id() << "拷贝构造" << endl; }~A() { cout << "析构" << this << "thread id= " << std::this_thread::get_id() << endl; }void thread_work(int num){cout << "thread_work" << endl;}
};int main()
{A myobj(10);std::thread myThread(&A::thread_work, myobj, 15);//有拷贝构造产生//std::thread myThread(&A::thread_work, std::ref(myobj), 15);//没有拷贝构造,不能用detach();if (myThread.joinable()) {myThread.join(); //myThread.detach();//一定不能是detach}return 0;
}
4. 扩展:std::this_thread类
namespace this_thread {thread::id get_id() noexcept;inline void yield() noexcept;void sleep_until(const xtime* _Abs_time) ;void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time)
};
4.1 get_id()
作用同 std::thread::get_id(); 获取线程ID值;
void threadFunc3()
{std::cout << std::this_thread::get_id() << endl;//32320
}int main()
{std::thread myThread(threadFunc3);std::cout << myThread.get_id() << std::endl;//32320if (myThread.joinable()) {myThread.join(); }return 0;
}
4.2 yield
yield参考
4.3 sleep_until
阻塞当前正在执行的线程直到sleep_time溢出。 参考
4.4 sleep_for
延时一段时间,支持ns到hour;
auto start = std::chrono::high_resolution_clock::now();std::this_thread::sleep_for(std::chrono::nanoseconds(1000));//1000ns
std::this_thread::sleep_for(std::chrono::microseconds(1000));//1000us
std::this_thread::sleep_for(std::chrono::milliseconds(1000));//1000ms
std::this_thread::sleep_for(std::chrono::seconds(5));//5sstd::chrono::duration<double, std::milli> elapsed = std::chrono::high_resolution_clock::now() - start;std::cout << elapsed.count() << endl;