• 2022-07-29 C++并发编程(三)



    前言

    对于某些并发程序,不涉及用线程梳理流程问题,只涉及并发运算,那么其实际使用线程不应超过cpu内核数量,否则并不会提高效率。

    C++给每个线程实现了单一id,如果将线程设计为不同的使用目的,可通过线程id进行区别。


    一、选择合适的线程数量

    C++实现了线程数量函数,

        const uint32_t hardware_threads = std::thread::hardware_concurrency();
    
    • 1

    用以确定cpu内核数量,分配最佳线程数量。

    每分配一个线程,都会消耗相应的资源,包括内存,cpu算力。

    当线程数量等于cpu内核数量,cpu算力效率达到最高,再增加线程数量,只会使得cpu算力下降,因为算力会分出相当部分用于同一内核间线程切换。

    所以某些并发算法需要控制实际使用线程数量,而非一味增加线程。

    #include 
    #include 
    #include 
    #include 
    
    //用于每个线程的累加计算
    template <typename Iterator, typename T>
    struct accumulateBlock
    {
        void operator()(Iterator first, Iterator last, T &result)
        {
            result = std::accumulate(first, last, result);
        }
    };
    
    //多线程累计计算算法
    template <typename Iterator, typename T>
    auto parallelAccumulate(Iterator first, Iterator last, T init) -> T
    {
        uint32_t const length = std::distance(first, last);
        if (!length)
        {
            return init;
        }
    
        const uint32_t minPerThread = 25;
        const uint32_t maxThreads = (length + minPerThread - 1) / minPerThread;
    
        //取得cpu内核数量,如系统不支持,则为0
        const uint32_t hardwareThreads = std::thread::hardware_concurrency();
    
        //取得真正使用的线程数量
        const uint32_t numThreads =
            std::min(hardwareThreads != 0 ? hardwareThreads : 2, maxThreads);
    
        const uint32_t blockSize = length / numThreads;
    
        //装载每个线程计算结果,用于最后累计
        std::vector<T> results(numThreads);
        //装载线程容器
        std::vector<std::thread> threads(numThreads - 1);
    
        Iterator blockStart = first;
    
        //开启线程进行累计计算
        for (uint32_t i = 0; i != (numThreads - 1); ++i)
        {
            Iterator blockEnd = blockStart;
            std::advance(blockEnd, blockSize);
            threads[i] = std::thread(accumulateBlock<Iterator, T>(), blockStart,
                                     blockEnd, std::ref(results[i]));
            blockStart = blockEnd;
        }
    
        //主线程进行最后一块的累计计算
        accumulateBlock<Iterator, T>()(blockStart, last, results[numThreads - 1]);
    
        //等待所有线程完成
        for (auto &entry : threads)
        {
            entry.join();
        }
    
        return std::accumulate(results.begin(), results.end(), init);
    }
    
    auto main(int /*unused*/, char * /*argv*/[]) -> int
    {
        std::vector<int> test(50);
    
        int cnt = 0;
    
        for (int i = 0; i != 50; ++i)
        {
            test[i] = i;
            cnt += i;
        }
    
        auto result = parallelAccumulate(test.begin(), test.end(), 0);
    
        std::cout << result << ' ' << cnt << std::endl;
    
        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
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84

    二、使用线程ID识别线程

    有时候我们需要用线程梳理业务逻辑,此时就需要识别每一个线程,使用C++线程id,非常容易。

    以下是用于分辨主线程的示例。

    #include 
    #include 
    
    std::thread::id masterThread = std::this_thread::get_id();
    
    void doMasterThreadWork();
    
    void doCommonWork();
    
    void someCorePartOfAlgorithm()
    {
        if (std::this_thread::get_id() == masterThread)
        {
            doMasterThreadWork();
        }
        doCommonWork();
    }
    
    auto main(int /*unused*/, char * /*argv*/[]) -> int
    {
        someCorePartOfAlgorithm();
        std::thread t(someCorePartOfAlgorithm);
        t.join();
        return 0;
    }
    
    void doMasterThreadWork()
    {
        std::cout << "master" << std::this_thread::get_id() << std::endl;
    }
    
    void doCommonWork()
    {
        std::cout << "common" << std::this_thread::get_id() << std::endl;
    }
    
    • 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

    总结

    设置合适的线程数量,以榨取最大cpu算力,辨别线程id,以梳理业务逻辑。

    使用C++线程库,很容易实现。

  • 相关阅读:
    港口AI叉车自动化辅助驾驶系统
    电商后台项目 + 源码
    spring---第六篇
    Python读取postgresql数据库
    数据结构与算法——14.栈
    经典算法之二分法
    Docker实战:docker compose 搭建Rocketmq
    爬虫爬取百度图片、搜狗图片
    【吴恩达机器学习笔记】十、支持向量机
    webpack的构建流程是什么?从读取配置到输出文件
  • 原文地址:https://blog.csdn.net/m0_54206076/article/details/126047984