C++ 并发编程实战: 第2章 线程管理

419 阅读3分钟

第2章 线程管理

本章内容

学习通过 std::thread 对象管理线程

  • thread 源码
  • thread 创建与销毁
  • 参数传递
  • joindetach
  • 线程转移

1. std::thread 源码

thread 结构:

数据成员

  • thr

    • id : 线程 id
    • handle : win32 句柄

函数成员

  • 构造

    • 默认构造
    • 线程函数参数构造
  • 支持移动操作

  • 删除拷贝操作

  • joinable, joindetach

  • swap, get_idhardware_concurrency

class thread
{
private:
    struct identifier
    { 
        void* handle;     
        unsigned int id;
    } thr;
​
public:
    thread() : thr{} {}                   
​
    template<class Fn, class... Args>
    explicit thread(Fn&& func, Args&&... args)      
    { 
        _Start(std::forward<Fn>(func), std::forward<Args>(args)...);
    }
​
    ~thread()  
    {
        if (joinable()) 
        {
            std::terminate();
        }
    }
​
​
    thread(thread&& other)  : thr(std::exchange(other.thr, {})) {}  
    thread& operator=(thread&& other)  
    {
        if (joinable()) 
        {
            std::terminate();
        }
​
        thr = std::exchange(other.thr, {});
        return *this;
    }
​
​
    thread(const thread&) = delete;
    thread& operator=(const thread&) = delete;
​
​
    bool joinable() const  
    {
        return thr.id != 0;
    }
​
    void join() 
    {
        if (!joinable())                                        // 1. 是否 joinable
        {
            _Throw_Cpp_error(_INVALID_ARGUMENT);
        }
​
        if (thr.id == _Thrd_id())                               // 2. 是否是当前线程
        {
            _Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);   // 资源死锁
        }
​
        if (_Thrd_join(thr, nullptr) != _Thrd_success)          // 3. 是否执行成功     
        {       
            _Throw_Cpp_error(_NO_SUCH_PROCESS);                 // 没有该线程
        }
​
        thr = {};
    }
​
    void detach() 
    {
        if (!joinable()) 
        {
            _Throw_Cpp_error(_INVALID_ARGUMENT);
        }
​
        _Check_C_return(_Thrd_detach(thr));
        
        thr = {};
    }
​
​
    void swap(thread& other)  
    {
        std::swap(thr, other.thr);
    }
​
    unsigned int get_id() const 
    {
        return thr.id;
    }
​
    static unsigned int hardware_concurrency();
};

2. thread 创建与销毁

构造

默认构造

thr{} 使得内置类型 thr.handlethr.id 都被初始化为 0

thread() : thr{} {}                   

函数构造

func 是线程要执行的函数, args 是该函数的参数

模板中的 && 为万能引用

std::forward 用于完美转发

万能引用 + 完美转发 = 将参数以原本的属性向下传递

template<class Fn, class... Args>
explicit thread(Fn&& func, Args&&... args)
{ 
    _Start(std::forward<Fn>(func), std::forward<Args>(args)...);
}

参数传递

thread 构造函数按值传递方式传递线程函数的参数

  • 实参先复制到线程栈空间, 再调用线程函数
void fun(int, char&) {}
​
char c = 'a';
​
thread t(fun, 1, c);            // 编译不通过
thread t(fun, 1, std::ref(c))   // 编译通过

类成员函数

传递对象指针

class A
{
public:
    void fun() {}
};
​
A a;
std::thread t(&A::fun, &a);

析构

析构时必须 非 joinable

等价说法:

  • 调用过 joindetach
  • 线程 id 为 0
~thread()  
{
    if (joinable()) 
    {
        std::terminate();
    }
}

3. thread 操作

joinable

查看是否还管理着线程

等价说法:

  • 线程是否能汇合
  • 线程 id 是否不为 0
bool joinable() const  
{
    return thr.id != 0;
}

join

A 执行 B.join()

B 阻塞 A, 直至 B 执行完

void join() 
{
    if (!joinable())            // B.id == 0                                        
    {
        _Throw_Cpp_error(_INVALID_ARGUMENT);
    }
​
    if (thr.id == _Thrd_id())   // B.id == A.id                               
    {
        _Throw_Cpp_error(_RESOURCE_DEADLOCK_WOULD_OCCUR);   
    }
​
    if (_Thrd_join(thr, nullptr) != _Thrd_success)  // 是否执行成功          
    {       
        _Throw_Cpp_error(_NO_SUCH_PROCESS);                 
    }
​
    thr = {};                   // B.id = B.handle = 0
}

问题:

  • A 执行 A.join() 会死锁

    A
    {
        A.join();   // A 阻塞 A
    }
    
  • A 执行 B.join(), B 执行 A.join() 会死锁

    A
    {
        B.join();   // B 阻塞 A
    }
    ​
    B
    {
        A.join();   // A 阻塞 B
    }
    

detach

将线程交给 C++ 运行时库管理

void detach() 
{
    if (!joinable()) 
    {
        _Throw_Cpp_error(_INVALID_ARGUMENT);
    }
​
    _Check_C_return(_Thrd_detach(thr));
​
    thr = {};
}

4. thread 支持移动操作

thread 支持移动构造移动赋值

thread(thread&& other)  : thr(std::exchange(other.thr, {})) {}  
thread& operator=(thread&& other)  
{
    if (joinable()) 
    {
        std::terminate();
    }
​
    thr = std::exchange(other.thr, {});
    return *this;
}

例:

void fun();
​
thread t1 = thread(fun);    
thread t2 = move(t1);       
t2 = thread(fun);       // 出错: t2.id 必须为 0

5. RAII 封装 thread

问题: thread 构造时满足 RAII 思想, 但析构不满足

class j_thread      
{
    std::thread t;
public:
    ...
    
    // 析构函数 RAII 释放资源
    ~j_thread()                         
    {
        if(t.joinable())
            t.join();
    }
}

总结

  • thread 数据成员: 句柄和线程 id

  • thread 函数成员

    • 构造析构
    • 支持移动操作
    • 删除拷贝操作
    • joinable, joindetach
    • swap, get_idhardware_concurrency
  • thread 构造函数将线程函数的参数按值传递方式传递
  • thread 不满足 RAII
  • joinable 判断线程 id 是否不为 0
  • join 阻塞, detach 非阻塞