zoukankan      html  css  js  c++  java
  • 详解c++多线程(四)

    C++中的原子操作

    一、atomic模版函数

    为了避免多个线程同时修改全局变量,C++11除了提供互斥量mutex这种方法以外,还提供了atomic模版函数。

    使用atomic可以避免使用锁,而且更加底层,比mutex效率更高。

    为了方便使用,c++11为模版函数提供了别名。

    atomic<bool>   别名:atomic_bool  

    atomic<int>  别名:atomic_int

    atomic<char> 别名:atomic_char 

    atomic<long> 别名:atomic_long 

     

    我们先来看一个例子:

    #include <thread>
    #include <iostream>
    #include <vector>
    #include <atomic>
    
    using namespace std;
    
    void func(int& counter)
    {
        for (int i = 0; i < 100000; ++i)
        {
            ++counter;
        }
    }
    
    int main()
    {
        //atomic<int> counter(0);
        int counter = 0;
        vector<thread> threads;
        for (int i = 0; i < 10; ++i)
        {
            threads.push_back(thread(func, ref(counter)));
        }
        for (auto& current_thread : threads)
        {
            current_thread.join();
        }
        cout << "Result = " << counter << '
    ';
        return 0;
    }

    输出结果:

     

    显然这个结果不是我们想要的,多跑几次就会发现,每一次的结果都会不一样。而这段代码的问题就在于多个线程同时修改了counter这个数导致出现错误。

    了解了前几章以后知道了锁可以用来解决这个问题,但是其实原子类型可以更加方便得解决这个问题。

    只需要把counter的原来的int型,改为atomic_int型就可以了,非常方便,也不需要用到锁。

     

    #include <thread>
    #include <iostream>
    #include <vector>
    #include <atomic>
    
    using namespace std;
    
    void func(atomic_int& counter)
    {
        for (int i = 0; i < 100000; ++i)
        {
            ++counter;
        }
    }
    
    int main()
    {
        //atomic<int> counter(0);
        atomic_int counter(0); //新建一个整型原子counter,将counter初始化为0
        vector<thread> threads;
        for (int i = 0; i < 10; ++i)
        {
            threads.push_back(thread(func, ref(counter)));
        }
        for (auto& current_thread : threads)
        {
            current_thread.join();
        }
        cout << "Result = " << counter << '
    ';
        return 0;
    }

    输出结果: 

    结果就正确了。

    二、std::atomic_flag

    std::atomic_flag是一个原子型的布尔变量,只有两个操作:

    1)test_and_set,如果atomic_flag 对象已经被设置了,就返回True,如果未被设置,就设置之然后返回False

    2)clear,把atomic_flag对象清掉

    注意这个所谓atomic_flag对象其实就是当前的线程。如果当前的线程被设置成原子型,那么等价于上锁的操作,对变量拥有唯一的修改权。

    调用clear就是类似于解锁。

    来看一个例子:

    #include <iostream>
    #include <atomic>
    #include <vector>
    #include <thread>
    #include <sstream>
    
    
    std::atomic_flag lock = ATOMIC_FLAG_INIT; //初始化原子flag
    std::stringstream  stream;
    
    void append_number(int x)
    {
        while(lock.test_and_set()); //如果原子flag未设置,那么返回False,就继续后面的代码。否则一直返回True,就一直停留在这个循环。
        stream<<"thread#" <<x<<'
    ';
        lock.clear(); //去除flag的对象
    }
    
    int main()
    {
        std::vector<std::thread> threads;
        for(int i=0;i<10;i++)
            threads.push_back(std::thread(append_number, i));
        
        for(auto& th:threads)
            th.join();
        std::cout<<stream.str()<<'
    ';
    }

    再看一个例子:

    #include <iostream>
    #include <atomic>
    #include <vector>
    #include <thread>
    #include <sstream>
    
    using namespace std;
    
    atomic<bool> ready(false);
    atomic_flag winner = ATOMIC_FLAG_INIT;
    
    void count1m(int id)
    {
        while(!ready) //如果ready=false,就会让当前线程一直在等待状态
            this_thread::yield();
        
        //此时ready为true
        for(int i=0;i<1000;i++);//数数
        
        //当有某个线程结束计数,然后被flag设置成了原子线程则返回false,于是执行打印id语句
        //由于并没有clear,所以该线程会一直是原子线程,而其他线程调用test_and_set就会一直返回True,于是不会执行后面的打印语句
        if(!winner.test_and_set())
            cout<<"winner thread id = "<<id<<endl;
    }
    
    int main()
    {
        vector<thread> threads;
        for(int i=0;i<10;i++)
            threads.push_back(thread(count1m, i));
        ready = true;
        
        for(auto &th:threads)
            th.join();
    }

    参考:

    https://blog.csdn.net/yhc166188/article/details/80572108

    https://www.cnblogs.com/taiyang-li/p/5914331.html

  • 相关阅读:
    【计网实验6】静态路由实验
    【计网】第六章
    【操统5】第六章/第七章
    【计网 6】链路层
    【Java学习1】
    【机器学习1】
    【计网实验】packet
    【计网】第五章网络层:控制平面
    python中math模块常用的方法整理
    使用python如何进行截小图
  • 原文地址:https://www.cnblogs.com/corineru/p/10867079.html
Copyright © 2011-2022 走看看