• 2022-07-27 C++并发编程(二)



    前言

    线程归属权转移,在C++中,线程只可移动不可拷贝,有明确归属权。


    一、线程移动

    线程只可move不可copy,对于线程,需要使用std::move(线程)进行转移。

    #include 
    #include 
    
    void some_function()
    {
        std::cout << 1 << std::endl;
    }
    
    void some_other_function()
    {
        std::cout << 2 << std::endl;
    }
    
    auto main(int /*unused*/, char * /*argv*/[]) -> int
    {
        //创建线程
        std::thread t1(some_function);
        //通过移动语义转移线程
        std::thread t2 = std::move(t1);
        //转移后的线程重新开启
        t1 = std::thread(some_other_function);
    
        std::thread t3;
        //通过move语义转移线程
        t3 = std::move(t2);
        //合并线程
        t1.join();
        //合并线程后移动线程
        t1 = std::move(t3);
        t1.join();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    我们可以向默认初始化的线程 t3 转移线程,可以向合并后的线程 t1 转移线程,也可以在线程初始化时 (t2) 转移线程,但不可以在已经持有资源的线程( 未被join或转移前的 t1 )转移线程,否则会导致程序崩溃。

        std::thread t1(some_function);
        std::thread t3(some_other_function);
    	//崩溃
        t1 = std::move(t3);
    
    • 1
    • 2
    • 3
    • 4

    二、通过函数转移线程

    由于函数返回结果是临时变量,所以可以用于转移线程

    std::thread f()
    {
    	//声明函数
        void some_fuction();
    	//返回线程
        return std::thread(some_fuction);
    }
    
    void some_fuction()
    {
        std::cout << 1 << std::endl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    三、用 RAII 思想封装生命周期内必然完结的线程的类

    对于C++,RAII 的思想也可以用于线程,保证 thread 变量生命周期结束前,线程必然完结。

    #include 
    #include 
    
    struct scopedThread
    {
        explicit scopedThread(std::thread t_)
            : t(std::move(t_))
        {
            if (!t.joinable())
            {
                throw std::logic_error("No thread");
            }
        }
        ~scopedThread()
        {
            t.join();
        }
        scopedThread(const scopedThread &) = delete;
        auto operator=(const scopedThread &) -> scopedThread & = delete;
    
      private:
        std::thread t;
    };
    
    struct func
    {
        explicit func(int &i_)
            : i(i_)
        {}
    
        void operator()() const
        {
            for (unsigned j = 0; j != 3; ++j)
            {
                fprintf(stdout, "%d%s\n", i, "test");
            }
        }
    
      private:
        int i;
    };
    
    void f()
    {
        int some_local_state;
        scopedThread t{std::thread(func(some_local_state))};
        for (int i = 0; i != 100; ++i)
        {
            std::cout << "_";
        }
    }
    
    auto main(int /*unused*/, char * /*argv*/[]) -> int
    {
        f();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57

    四、用容器装载 thread 变量

    支持移动语义的容器可以装载thread变量,根据需要创建线程。需要提醒的是,这并不一定比单线程效率高。

    void do_work(unsigned id)
    {
        std::cout << id << std::endl;
    }
    
    void f()
    {
        std::vector<std::thread> threads;
    
        for (unsigned i = 0; i != 20; ++i)
        {
            threads.emplace_back(do_work, i);
        }
    
        for (auto &entry : threads)
        {
            entry.join();
        }
    }
    
    auto main(int /*unused*/, char * /*argv*/[]) -> int
    {
        joining_thread test(print);
        f();
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    总结

    线程只支持移动,不支持拷贝,这是很容易理解的,也就是所谓的所有权转移,读者可简单练习体会。

  • 相关阅读:
    操作系统(Operating System)知识点复习——第九章 单处理器调度
    人类智能的精髓超出了统计概率
    WPF 截图工具
    ​python联欢会评分 青少年编程电子学会python编程等级考试三级真题解析2020年9月
    【Java】javadoc生成文档/用户交互Scanner
    【Go入门】 Go搭建一个Web服务器
    stack和queue简单实现(容器适配器)
    HHDBCS监控功能
    [剑指 Offer]数组中出现次数超过一半的数字
    python实现矩阵转置
  • 原文地址:https://blog.csdn.net/m0_54206076/article/details/126006873