c++ std::thread线程类

1. std::thread类

/*
源码来自vs2017
*/

using _Thrd_id_t = unsigned int;
struct _Thrd_t { // thread identifier for Win32
    void* _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 
    void _Start(_Fn&& _Fx, _Args&&... _Ax) {...}
	
public:
    template , 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=00000037DD95FA58

    if (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 ptr)
{
}

int main() 
{
    std::unique_ptr 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;//32320

    if (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));//5s

std::chrono::duration elapsed = std::chrono::high_resolution_clock::now() - start;
 

std::cout << elapsed.count() << endl;

你可能感兴趣的:(c++,c++)