[C/C++]_[初级]_[原子操作]

来源:互联网 发布:idea新建普通java项目 编辑:程序博客网 时间:2024/05/20 20:17

场景

1.现在多线程基本是开发应用必备的解决同步阻塞的解决方案,比如在分析日志文件时在界面显示分析进度;多线程修改或读取同一个变量来记录容器里的对象个数等等.如果是每次都使用互斥量对变量加锁,那么加解锁是非常耗费CPU时间的. 所以使用CPU提供的无锁指令是一个必然的选择.
2.互斥量的坏处之一就是容易造成死锁,所以一般只在重量级的容器对象才会加互斥锁.
比如:

pthread_mutex_lock

3.每个系统平台都提供了原子操作的相关系统函数.所谓原子操作,就是在做一个复合算术运算时使用内存屏障来保证运算能顺序执行.

This function generates a full memory barrier (or fence) to ensure that memory operations are completed in order.

4.注意,对于简单读写4字节对齐的32-bit变量在32-bit系统上是原子操作,对于简单读写8字节对齐的64-bit变量在64-bit系统上是原子操作. MSDN上说的真是含糊~,什么是 “properly-aligned”? 只能结合 InterlockedExchangeAdd 这类函数总结.

Simple reads and writes to properly‐aligned 32‐bit variables are atomic operations. In other words, you willnot end up with only one portion of the variable updated; all bits are updated in an atomic fashion. However,access is not guaranteed to be synchronized. If two threads are reading and writing from the same variable,you cannot determine if one thread will perform its read operation before the other performs its writeoperation.Simple reads and writes to properly aligned 64‐bit variables are atomic on 64‐bit Windows. Reads and writesto 64‐bit values are not guaranteed to be atomic on 32‐bit Windows. Reads and writes to variables of othersizes are not guaranteed to be atomic on any platform

5.注意VS里的结构体成员对齐并不是指每个成员都是指定的对齐,而是结构体大小是指定对齐的n的倍数.

6.注意,读对齐的32-bit变量不需要使用interlocked函数.

7.如果使用支持C++11 atomic库的编译器, 也可以直接使用这个库.

参考

1. Interlocked Variable Access
2. /Zp (Struct Member Alignment)

例子

// test_atomic.cpp : 定义控制台应用程序的入口点。//#include "stdafx.h"#include <Windows.h>#include <iostream>#include <memory>#include <string>#include <set>#include <assert.h>class MyAtomic{public:    MyAtomic():three(20){}    // Win32程序,需要4字节对齐,如果是64位程序,需要8字节对齐.    __declspec(align(4)) volatile LONG one;    volatile LONG two;    volatile unsigned long long three;};// 这个例子不太好,原子操作递增是不会出现重复值的.void TestAtomic(MyAtomic& ma){    // 赋值变量.    // 不需要32位地址对齐的赋值操作,如果不使用这个函数,直接操作32-bit的变量,需要32-bit对齐.    InterlockedExchange(&ma.two,100);    // 32-bit地址对齐,简单赋值不需要interlock    ma.one = 10;    // 递增变量:地址需要32-bit对齐.    // The variable pointed to by the Addend parameter must be aligned on a 32-bit boundary;    // otherwise, this function will behave unpredictably on multiprocessor x86 systems and any non-x86 systems    InterlockedIncrement(&ma.one);    // 加法运算    // 需要32-bit地址对齐.    InterlockedExchangeAdd(&ma.one,100);    // 比较并赋值    // 需要32-bit地址对齐.    InterlockedCompareExchange(&ma.one,50,111);    // &运算    // 不需要32-bit地址对齐.    InterlockedAnd(&ma.three,20);}DWORD WINAPI ST(LPVOID param){    auto pt =  (std::shared_ptr<MyAtomic>*)param;    MyAtomic* ic = pt->get();    TestAtomic(*ic);    std::cout << "ma.one " << ic->one << std::endl;    std::cout << "ma.two: " << ic->two << std::endl;    std::cout << "ma.three: " << ic->three << std::endl;    delete pt;    return 0;}int main(){    std::cout << "begin atomic" << std::endl;    MyAtomic* ma = new MyAtomic();    std::shared_ptr<MyAtomic> pt1(ma);    for(int i = 0; i< 100;++i)    {        std::shared_ptr<MyAtomic>* pt = new std::shared_ptr<MyAtomic>(pt1);        HANDLE h = CreateThread(NULL,0,ST,(void*)pt,0,0);        CloseHandle(h);    }    while(pt1.use_count() > 1)    {        Sleep(1000);        continue;    }    std::cout << "end atomic" << std::endl;}
0 0
原创粉丝点击