• c++ 原子操作(atomic)


    一. 一个简单的例子

    看以下代码:

    #include 
    #include 
    #include 
    #include 
    
    int i = 0;
    const int max_iter = 1000000;
    
    void mythread()
    {
        for (int j = 0; j < max_iter; j++)
        {
            i++;  //线程同时操作变量
        }
    }
    
    int main() {
        auto begin = std::chrono::high_resolution_clock::now();
        std::thread t1(mythread);
        std::thread t2(mythread);
        t1.join();
        t2.join();
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << "i=" << i << std::endl;
    
        std::cout << "time: "
                  << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() * 1e-6
                  << "s"
                  << 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

    对应的CMakeLists.txt

    cmake_minimum_required(VERSION 3.18)
    project(smart_ptr)
    
    set(CMAKE_CXX_STANDARD 11)
    
    add_executable(smart_ptr main.cpp)
    
    
    SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -std=c++11 -pthread")
    SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -std=c++11 -pthread")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    运行结果:

    i=120352
    time: 0.000658s
    
    • 1
    • 2

    怎么回事,结果竟然不是200000?

    二. 填坑(加锁)

    i++这一条程序在计算机中是分几个机器指令来执行的,先把i值赋值给eax寄存器,eax寄存器自加1,然后再把eax寄存器值赋值回i,如果在指令执行过程中发生了线程调度,那么这一套完整的i++指令操作被打断,会发生结果错乱。
    想到的解决方法是加锁,看下面的代码:

    #include 
    #include 
    #include 
    #include 
    #include 
    
    int i = 0;
    const int max_iter = 100000;
    std::mutex mut;
    
    void mythread()
    {
        for (int j = 0; j < max_iter; j++)
        {
            mut.lock();  // 加锁操作
            i++;  //线程同时操作变量
            mut.unlock();
        }
    }
    
    int main() {
        auto begin = std::chrono::high_resolution_clock::now();
        std::thread t1(mythread);
        std::thread t2(mythread);
        t1.join();
        t2.join();
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << "i=" << i << std::endl;
    
        std::cout << "time: "
                  << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() * 1e-6
                  << "s"
                  << 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

    运行结果:

    i=200000
    time: 0.010806s
    
    • 1
    • 2

    结果对了,但是耗时好像增加了。

    三. 优化(原子操作

    所谓原子操作,就是多线程程序中“最小的且不可并行化的”操作。对于在多个线程间共享的一个资源而言,这意味着同一时刻,多个线程中有且仅有一个线程在对这个资源进行操作,即互斥访问。

    #include 
    #include 
    #include 
    #include 
    
    
    std::atomic<int>i(0);   // std::atomic_int32_t i(0);  原子操作的另一种写法
    const int max_iter = 100000;
    
    void mythread()
    {
        for (int j = 0; j < max_iter; j++)
        {
            i++;  //线程同时操作变量
        }
    }
    
    int main() {
        auto begin = std::chrono::high_resolution_clock::now();
        std::thread t1(mythread);
        std::thread t2(mythread);
        t1.join();
        t2.join();
        auto end = std::chrono::high_resolution_clock::now();
        std::cout << "i=" << i << std::endl;
    
        std::cout << "time: "
                  << std::chrono::duration_cast<std::chrono::microseconds>(end - begin).count() * 1e-6
                  << "s"
                  << 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

    运行结果:

    i=200000
    time: 0.003898s
    
    • 1
    • 2

    哈哈,结果正确,运行速度提升了3倍,不错!

    四.采坑记录

    刚开始编译的时候报错:对‘pthread_create’未定义的引用
    原来多线程要在CMakeLists.txt的最后加上两句指令:

    SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g -ggdb -std=c++11 -pthread")
    SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall -std=c++11 -pthread")
    
    • 1
    • 2

    总结:用原子操作可以保证结果的正确性,并且运行效率比加锁的效率高!!!

  • 相关阅读:
    RHCE学习 --- 第一次作业
    Express框架
    基于Pytorch的驾驶员分心行为实时检测
    千年TGS服务器日志报错如何解决
    R语言dplyr包mutate_all函数将dataframe中的所有数值数值列(变量)乘以某一固定值并生成新的数据列,为新的数据列(变量)指定自定义后缀名称
    tcr历史比赛竞赛规则
    方舟开服配置教程服务器怎么开
    Python基础复习-基本数据类型
    [Python进阶] 操纵鼠标:PyAutoGUI
    Flink系列文档-(YY01)-初识Flink
  • 原文地址:https://blog.csdn.net/Guo_Python/article/details/127729969