2.线程归属权

线程归属权

线程归属权是基于C++的thread库来讲的,线程库要求创建一个thread类型的变量来表示一个线程,那么该线程的所属权就是该thread变量

线程所属权的改变

  1. 使用std::move来移动线程变量,将其转移给其他线程变量
  2. 该线程使用detach方法,让线程在后台运行,此时线程的所属权转交给后台

线程所属权的注意点

不要对已经绑定的线程进行另一个线程所属权的赠与,也就是一个线程只能执行一个任务,不能同时管理两个线程,否则会触发terminate

是因为当一个正在执行的线程被赠与另一个线程的所有权,那么就会造成原线程的终止,从而出发析构函数的terminate

void some_function() {
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}
void some_other_function() {
    while (true) {
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
}

int main() {
    std::thread t1(some_function);
    std::thread t2 = std::move(t1);
    t1 = std::thread(some_other_function);
    std::thread t3;
    t3 = std::move(t2);
    std::this_thread::sleep_for(std::chrono::seconds(2000));
    return 0;
}

joining_thread的封装

该封装的目的是在析构函数中自动汇合

class joining_thread{
    std::thread _t;
public:
    joining_thread() = default;
    template<typename Callable , typename ...Args>
    explicit joining_thread(Callable&& Fn , Args&& ... args)
    :_t(std::forward<Callable>(Fn),std::forward<Args>(args)...)
    {}
    explicit joining_thread(std::thread t) noexcept: _t(std::move(t)){}
    joining_thread(joining_thread&& other) noexcept:_t(std::move(other._t)){}
     joining_thread& operator=(joining_thread&& other) noexcept
    {
        //如果当前线程可汇合,则汇合等待线程完成再赋值
        if (joinable()) {
            join();
        }
        _t = std::move(other._t);
        return *this;
    }
    joining_thread& operator=(joining_thread other) noexcept
    {
        //如果当前线程可汇合,则汇合等待线程完成再赋值
        if (joinable()) {
            join();
        }
        _t = std::move(other._t);
        return *this;
    }
    ~joining_thread() noexcept {
        if (joinable()) {
            join();
        }
    }
    void swap(joining_thread& other) noexcept {
        _t.swap(other._t);
    }
    std::thread::id   get_id() const noexcept {
        return _t.get_id();
    }
    bool joinable() const noexcept {
        return _t.joinable();
    }
    void join(){
        _t.join();
    }
    void detach(){
        _t.detach();
    }
     std::thread& as_thread() noexcept {
        return _t;
    }
    const std::thread& as_thread() const noexcept {
        return _t;
    }
};

容器存储

下面是使用容器的两种等价的写法

void use_vector(){
    std::vector<std::thread> threads;
    for(int i = 0 ; i < 2 ; i ++)
    {
        std::thread t(thread_worker);
        threads.push_back(std::move(t));
        threads.emplace_back(thread_worker);
    }
}

std::move的一个知识点

在一个类没有实现移动构造函数但是实现了拷贝构造函数时,构造该类对象传入std::move(对象)会调用类的拷贝构造

多线程的应用:并行计算

template<typename Iterator, typename T>
struct accumulate_block{
  void operator()(Iterator first , Iterator last , T& result){
      result = std::accumulate(first,last,result);
  }  

};

template<typename Iterator, typename T>
T parallel_accumulate(Iterator first, Iterator last, T init)
{
    unsigned long const length = std::distance(first, last);
    if (!length)
        return init;    //⇽-- - ①
        unsigned long const min_per_thread = 25;
    unsigned long const max_threads =
        (length + min_per_thread - 1) / min_per_thread;    //⇽-- - ②
        unsigned long const hardware_threads =
        std::thread::hardware_concurrency();
    unsigned long const num_threads =
        std::min(hardware_threads != 0 ? hardware_threads : 2, max_threads);    //⇽-- - ③
        unsigned long const block_size = length / num_threads;    //⇽-- - ④
        std::vector<T> results(num_threads);
    std::vector<std::thread>  threads(num_threads - 1);   // ⇽-- - ⑤
        Iterator block_start = first;
    for (unsigned long i = 0; i < (num_threads - 1); ++i)
    {
        Iterator block_end = block_start;
        std::advance(block_end, block_size);    //⇽-- - ⑥
            threads[i] = std::thread(//⇽-- - ⑦
                accumulate_block<Iterator, T>(),
                block_start, block_end, std::ref(results[i]));
        block_start = block_end;    //⇽-- - ⑧
    }
    accumulate_block<Iterator, T>()(
        block_start, last, results[num_threads - 1]);    //⇽-- - ⑨
        for (auto& entry : threads)
            entry.join();    //⇽-- - ⑩
            return std::accumulate(results.begin(), results.end(), init);    //⇽-- - ⑪
}
void use_parallel_acc() {
    std::vector <int> vec(10000);
    for (int i = 0; i < 10000; i++) {
        vec.push_back(i);
    }
    int sum = 0;
    sum = parallel_accumulate<std::vector<int>::iterator, int>(vec.begin(), 
        vec.end(), sum);
    std::cout << "sum is " << sum << std::endl;
}