首页 > 技术文章 > atomic原子操作

ljygoodgoodstudydaydayup 2016-10-13 19:48 原文

C++中对共享数据的存取在并发条件下可能会引起data race的未定义行为,需要限制并发程序以某种特定的顺序执行,有两种方式:1.使用mutex保护共享数据; 2.原子操作

原子操作:针对原子类型操作要不一步完成,要么不做,不可能出现操作一半被切换CPU,这样防止由于多线程指令交叉执行带来的可能错误。非原子操作下,某个线程可能看见的是一个其它线程操作未完成的数据。

一、atomic_flag

std::atomic_flag是一个bool原子类型,有两个状态:set(flag=true) 和 clear(flag=false),必须被ATOMIC_FLAG_INIT初始化,此时flag为clear状态,相当于静态初始化。

一旦atomic_flag初始化后只有三个操作:test_and_set,clear,析构,均是原子化操作。

test_and_set检查flag是否被设置,若被设置直接返回true,若没有设置则设置flag为true后再返回false。

clear()清除flag标志即flag=false。

不支持拷贝、赋值等操作,这和所有atomic类型一样,因为两个原子类型之间操作不能保证原子化。atomic_flag的可操作性不强导致其应用局限性,还不如atomic<bool>。

使用atomic_flag作为简单的自旋锁例子:每个线程先将atomic_flag设为true,再设为false

atomic_flag lock_stream = ATOMIC_FLAG_INIT;//flag处于clear状态,没有被设置过  
stringstream stream;

void append_number(int x) 
{
    while (lock_stream.test_and_set()) {}//检查并设置是个原子操作,如以前没有设置过则退出循环,  
                                         //每个线程都等待前面一个线程将lock_stream状态清楚后跳出循环,第一个线程除外,此时lock_stream为clear状态  
    stream << "thread #" << x << '\n';
    lock_stream.clear();
}

int main()
{
    vector<std::thread> threads;
    for (int i = 1; i <= 10; ++i)
        threads.push_back(thread(append_number, i));
    for (auto& th : threads) 
        th.join(); 
    cout << stream.str();

    system("pause");
    return 0;
}

二、 atomic<T>模板类

atomic<T>生成一个T类型的原子对象,并提供了系列原子操作函数。其中T是trivially  copyable type,满足:要么全部定义了拷贝/移动/赋值函数,要么全部没定义;没有虚成员;基类或其它任何非static成员都是trivally copyable。典型的内置类型bool、int等属于trivally copyable。再如class triviall{public: int x};也是。

struct Node 
{ 
    int value; 
    Node* next; 
    Node(int val,Node* p):value(val),next(p){}
};

atomic<Node*> list_head;

void append(int val) 
{     // append an element to the list  
    Node* newNode = new Node(val,list_head );
    // next is the same as: list_head = newNode, but in a thread-safe way:  
    while (!list_head.compare_exchange_weak(newNode->next, newNode)) {}
    // (with newNode->next updated accordingly if some other thread just appended another node)  
}

int main()
{
    // spawn 10 threads to fill the linked list:  
    vector<thread> threads;
    for (int i = 0; i<10; ++i) 
        threads.push_back(thread(append, i));
    for (auto& th : threads) th.join();
    // print contents:  
    for (Node* it = list_head; it != nullptr; it = it->next)
        cout << ' ' << it->value;
    std::cout << '\n';
    // cleanup:  
    Node* it; while (it = list_head) { list_head = it->next; delete it; }

    system("pause");
    return 0;
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

http://blog.csdn.net/liuxuejiang158blog/article/details/17413149

 

推荐阅读