C++11原子操作详解

目录
  • C++11原子操作
    • 原子操作的概念
    • 示例
  • 总结

C++11原子操作

原子操作的概念

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

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

示例

#include <iostream>
#include <thread>//C++11线程库且跨平台
#include <windows.h>//Sleep函数需要使用的库文件
using std::cout;
using std::endl;
using std::cin;
int g_total = 0;
void click()
{
	for (int i = 0; i < 1000000; i++)
	{
		g_total++;
	}
}

int main()
{
	for (int i = 0; i < 4; i++)
	{
		std::thread t(click);
		t.detach();
	}
	Sleep(1000);
	cout << "result:" << g_total << endl;
	return 0;
}

我们很正常的认为这样做是可以提高CPU的利用效率的,但是实际上执行结果并不正确。

紧接着,我们肯定想到使用互斥锁对共享资源进行保护。

#include <iostream>
#include <thread>//C++11线程库是跨平台的
#include <mutex>//C++11互斥锁
#include <windows.h>//Sleep函数需要使用的库文件
using std::cout;
using std::endl;
using std::cin;

int g_total = 0;
std::mutex g_mutex;
void click()
{
	for (int i = 0; i < 1000000; i++)
	{
		g_mutex.lock();//访问之前锁定互斥对象
		g_total++;
		g_mutex.unlock();//访问之后释放互斥对象
	}
}

int main()
{
	for (int i = 0; i < 4; i++)
	{
		std::thread t(click);
		t.detach();
	}
	Sleep(1000);
	cout << "result:" << g_total << endl;
	return 0;
}

初始对象的使用,保证同一时刻只有唯一一个线程对这个共享对象进行访问。

在C++11之前,互斥锁的概念已经足够了,但是在C++11提出之后,进一步利用CPU性能。在C++11中实现了原子操作的数据类型(如atomic_bool,atomic_int等)。使用原子操作的数据类型线程对其进行访问的时候无需借助mutex等锁机制,也能实现对共享资源的正确访问。

#include <iostream>
#include <thread>//C++11线程库是跨平台的
#include <atomic>//C++11原子操作库
#include <windows.h>//Sleep函数需要使用的库文件
using std::cout;
using std::endl;
using std::cin;
std::atomic_int g_total = 0;//使用原子操作的数据类型
void click()
{
	for (int i = 0; i < 1000000; i++)
	{
		//mutex.lock();
		g_total++;
		//mutex.unlock();
	}
}

int main()
{
	for (int i = 0; i < 4; i++)
	{
		std::thread t(click);
		t.detach();
	}
	Sleep(1000);
	cout << "result:" << g_total << endl;
	return 0;
}

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 利用C++11原子量如何实现自旋锁详解

    一.自旋锁 自旋锁是一种基础的同步原语,用于保障对共享数据的互斥访问.与互斥锁的相比,在获取锁失败的时候不会使得线程阻塞而是一直自旋尝试获取锁.当线程等待自旋锁的时候,CPU不能做其他事情,而是一直处于轮询忙等的状态.自旋锁主要适用于被持有时间短,线程不希望在重新调度上花过多时间的情况.实际上许多其他类型的锁在底层使用了自旋锁实现,例如多数互斥锁在试图获取锁的时候会先自旋一小段时间,然后才会休眠.如果在持锁时间很长的场景下使用自旋锁,则会导致CPU在这个线程的时间片用尽之前一直消耗在无意义的忙等

  • C++11并发编程关于原子操作atomic的代码示例

    一:概述 项目中经常用遇到多线程操作共享数据问题,常用的处理方式是对共享数据进行加锁,如果多线程操作共享变量也同样采用这种方式. 为什么要对共享变量加锁或使用原子操作?如两个线程操作同一变量过程中,一个线程执行过程中可能被内核临时挂起,这就是线程切换,当内核再次切换到该线程时,之前的数据可能已被修改,不能保证原子操作. C++11提供了个原子的类和方法atomic,保证了多线程对变量原子性操作,相比加锁机制mutex.lock(),mutex.unlock(),性能有几倍的提升. 所需头文件<a

  • 详解C++11原子类型与原子操作

    1.认识原子操作 原子操作就是在多线程程序中"最小的且不可并行化的"操作,意味着多个线程访问同一个资源时,有且仅有一个线程能对资源进行操作.通常情况下原子操作可以通过互斥的访问方式来保证,例如Linux下的互斥锁(mutex),Windows下的临界区(Critical Section)等.下面看一个Linux环境使用POSIX标准的pthread库实现多线程下的原子操作: #include <pthread.h> #include <iostream> usi

  • C++11中的原子量和内存序详解

    一.多线程下共享变量的问题 在多线程编程中经常需要在不同线程之间共享一些变量,然而对于共享变量操作却经常造成一些莫名奇妙的错误,除非老老实实加锁对访问保护,否则经常出现一些(看起来)匪夷所思的情况.比如下面便是两种比较"喜闻乐见"的情况. (a) i++问题 在多线程编程中,最常拿来举例的问题便是著名的i++ 问题,即:多个线程对同一个共享变量i执行i++ 操作.这样做之所以会出现问题的原因在于i++这个操作可以分为三个步骤: step operation 1 i->reg(读取

  • C++11原子操作详解

    目录 C++11原子操作 原子操作的概念 示例 总结 C++11原子操作 原子操作的概念 所谓原子操作,其意义就是"原子是最小的,不可分割的最小个体".**表示当多个线程访问同一个全局资源的时候,能够确保所有其它的线程都不在同一时间访问相同的资源.**也就是它确保在同一时刻只有唯一的线程对这个资源进行访问.类似于共享资源的访问保护.但是原子操作更加接近底层,即效率更高. 在以往C++中没有原子操作的规定,更多使用的都是汇编语言或者借助第三方库,如Intel的pthread来实现.但在C

  • Go语言并发之原子操作详解

    目录 修改 赋值与读取 比较并交换 小结 代码中的加锁操作因为涉及内核态的上下文切换会比较耗时.代价比较高.针对基本数据类型我们还可以使用原子操作来保证并发安全,因为原子操作是Go语言提供的方法它在用户态就可以完成,因此性能比加锁操作更好.Go语言中原子操作由内置的标准库sync/atomic 提供. 大多数情况下我们都是针对基本数据类型进行数据操作,能不加锁就不加锁. 首先很多人都不相信基本类型并发修改会出现竞态问题.不妨尝试一下,并发加一. var wg sync.WaitGroup for

  • iOS 11 safeArea详解及iphoneX 适配

    最近看了许多iPhone X适配的文章,发现很少有介绍safeArea的,就来随便写写 现在对于iPhone X的适配,有一种常见的做法是给导航栏或tabbar增加一个固定的距离,比如顶部增加44pt,底部增加34pt.这种写死距离的做法乍看上去挺简单,其实并不好,理由如下 不适合多机型的适配,如果以后出了一种带刘海的iPad,需要预留出来的距离就未必是现在写死的距离 不适合需要支持横竖屏的app,横屏顶部不需要增加距离,反而是左右各有44pt,底部的距离也和竖屏不同 不够动态.还是举个例子,假

  • C++11 std::shared_ptr总结与使用示例代码详解

    最近看代码,智能指针用的比较多,自己平时用的少,周末自己总结总结.方便后续使用. std::shared_ptr大概总结有以下几点: (1) 智能指针主要的用途就是方便资源的管理,自动释放没有指针引用的资源. (2) 使用引用计数来标识是否有多余指针指向该资源.(注意,shart_ptr本身指针会占1个引用) (3) 在赋值操作中, 原来资源的引用计数会减一,新指向的资源引用计数会加一. std::shared_ptr<Test> p1(new Test); std::shared_ptr&l

  • 详解C++之C++11的牛逼特性

    一.列表初始化 1.1 C++98中,标准允许使用花括号{}对数组元素进行统一的列表初始值设定. int array1[] = {1,2,3,4,5}; int array2[] = {0}; 对对于一些自定义类型,却不行. vector<int> v{1,2,3,4,5}; 在C++98中这样无法通过编译,因此需要定义vector之后,在使用循环进行初始赋值. C++11扩大了用初始化列表的使用范围,让其适用于所有的内置类型和自定义类型,而且使用时,=可以不写 // 内置类型 int x1

  • c++11 新特性——智能指针使用详解

    c++11添加了新的智能指针,unique_ptr.shared_ptr和weak_ptr,同时也将auto_ptr置为废弃(deprecated). 但是在实际的使用过程中,很多人都会有这样的问题: 不知道三种智能指针的具体使用场景 无脑只使用shared_ptr 认为应该禁用raw pointer(裸指针,即Widget*这种形式),全部使用智能指针 初始化方法 class A { public: A(int size){ this->size = size; } A(){} void Sh

  • c++11 atomic的使用详解

    std::atomic_flag std::atomic_flag是一个原子的布尔类型,可支持两种原子操作: test_and_set, 如果atomic_flag对象被设置,则返回true; 如果atomic_flag对象未被设置,则设置之,返回false clear. 清楚atomic_flag对象 std::atomic_flag可用于多线程之间的同步操作,类似于linux中的信号量.使用atomic_flag可实现mutex. #include <iostream> #include

  • 详解C++11中的线程库

    目录 一.线程库的介绍 1.1. 使用时的注意点 1.2. 线程函数参数 1.3. join与detach 二.原子性操作库 2.1. atomic 2.2. 锁 三.使用lambda表达式创建多个线程 四.条件变量 一.线程库的介绍 在C++11之前,涉及到多线程问题,都是和平台相关的,比如windows和linux下各有自己的接口,这使得代码的可移植性比较差.C++11中最重要的特性就是对线程进行支持了,使得C++在并行编程时不需要依赖第三方库,而且在原子操作中还引入了原子类的概念.要使用标

  • GO的锁和原子操作的示例详解

    目录 GO的锁和原子操作分享 锁是什么 锁是用来做什么的 互斥锁 互斥锁 - 解决问题 读写锁 我们先来写一个读写锁的DEMO 自旋锁和互斥锁的区别 如何选择锁 啥是原子操作 总结 GO的锁和原子操作分享 上次我们说到协程,我们再来回顾一下: 协程类似线程,是一种更为轻量级的调度单位 线程是系统级实现的,常见的调度方法是时间片轮转法 协程是应用软件级实现,原理与线程类似 协程的调度基于 GPM 模型实现 要是对协程的使用感兴趣的话,可以看看这篇文章简单了解一下瞅一眼就会使用GO的并发编程分享 今

随机推荐