您的位置:首页 > 编程语言 > C语言/C++

c++ 11 多线线程系列-----------原子操作(atomic operation)

2015-11-15 19:34 465 查看
  c++ 11中对原子的介绍:Atomic---Atomic types are
types that encapsulate a value whose access is guaranteed to not cause data races and can be used to synchronize memory accesses among different threads.

  所谓的原子操作,取的就是“原子是最小的、不可分割的最小个体”的意义,它表示在多个线程访问同一个全局资源的时候,能够确保所有其他的线程都不在同一时间内访问相同的资源。也就是他确保了在同一时刻只有唯一的线程对这个资源进行访问。这有点类似互斥对象对共享资源的访问的保护,但是原子操作更加接近底层,因而效率更高。

  在以往的C++标准中并没有对原子操作进行规定,我们往往是使用汇编语言,或者是借助第三方的线程库,例如intel的pthread来实现。在新标准C++11,引入了原子操作的概念,并通过这个新的头文件提供了多种原子操作数据类型,例如,atomic_bool,atomic_int等等,如果我们在多个线程中对这些类型的共享资源进行操作,编译器将保证这些操作都是原子性的,也就是说,确保任意时刻只有一个线程对这个资源进行访问,编译器将保证,多个线程访问这个共享资源的正确性。从而避免了锁的使用,提高了效率。

  我们来看一个计数程序:

#include <iostream>
#include <thread>
#include <atomic>
#include <time.h>

long cnt = 0;

void counter()
{
for (int i = 0; i<100000; ++i)
{
++cnt;
}
}

int main(int argc, char* argv[])
{
clock_t start = clock();//计时

std::thread threads[100];
for (int i = 0; i != 100; ++i)
{
threads[i] = std::thread(counter);
}

for (auto &th : threads)
th.join();

clock_t finish = clock();

std::cout << "result:" << cnt << std::endl;
std::cout << "duration:" << finish - start << "ms" << std::endl;
return 0;
}




  分析:虽然时间很快,但是计算的结果是错误的,因为上述线程之间在同一时间内去访问了全局变量cnt,很快我们是不是有想到了利用利用之前博文里面讲到互斥量来保护全局资源的访问。那么不妨我们来试试。代码如下

  我把上面的代码100000改成10000,因为在我的电脑上1000000把cpu跑死了,看下面输出结果,虽然计算结果是正确的,但是利用互斥量保护共享资源造成很大的损失了。

#include <iostream>
#include <thread>
#include <atomic>
#include <time.h>
#include <mutex>

std::mutex mtx;

long cnt = 0;

void counter()
{
//mtx.lock();
for (int i = 0; i<10000; ++i)
{
mtx.lock();
cnt += 1;
mtx.unlock();
}
}

int main(int argc, char* argv[])
{
clock_t start = clock();//计时

std::thread threads[100];
for (int i = 0; i != 100; ++i)
{
threads[i] = std::thread(counter);
}

for (auto &th : threads)
th.join();

clock_t finish = clock();

std::cout << "result:" << cnt << std::endl;
std::cout << "duration:" << finish - start << "ms" << std::endl;
return 0;
}



  
  如果是在C++11之前,我们的解决方案也就到此为止了,但是,C++对性能的追求是永无止境的,他总是想尽一切办法榨干CPU的性能。在C++11中,实现了原子操作的数据类型(atomic_bool,atomic_int,atomic_long等等),对于这些原子数据类型的共享资源的访问,无需借助mutex等锁机制,也能够实现对共享资源的正确访问。

#include <iostream>
#include <thread>
#include <atomic>
#include <time.h>
#include <mutex>

std::mutex mtx;

std::atomic_long cnt;

void counter()
{
for (int i = 0; i<100000; ++i)
{
cnt += 1;
}
}

int main(int argc, char* argv[])
{
clock_t start = clock();//计时

std::thread threads[100];
for (int i = 0; i != 100; ++i)
{
threads[i] = std::thread(counter);
}

for (auto &th : threads)
th.join();

clock_t finish = clock();

std::cout << "result:" << cnt << std::endl;
std::cout << "duration:" << finish - start << "ms" << std::endl;
return 0;
}




   结果正确!耗时比较利用mutex少了很多!也仅仅是不采用任何保护机制的时间的10倍。我的电脑垃圾,在你的电脑上还快些,除非你的电脑比我电脑还垃圾,可以说这是一个非常不错的成绩了。

   原子操作的实现跟普通数据类型类似,但是它能够在保证结果正确的前提下,提供比mutex等锁机制更好的性能,如果我们要访问的共享资源可以用原子数据类型表示,那么在多线程程序中使用这种新的等价数据类型,是一个不错的选择。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: