DoubleLi

qq: 517712484 wx: ldbgliet

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  4737 随笔 :: 2 文章 :: 542 评论 :: 1615万 阅读
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

 

【并发编程十四】c++原子操作(2)——实现自旋锁

 

  • 简介
    在介绍完原子操作,我们这篇使用c++提供的原子操作,实现一个自旋锁,并加以利用。

原子操作参见【并发编程十三】c++原子操作

一、自旋锁简介

“自旋”可以理解为“自我旋转”,这里的“旋转”指“循环”,比如 while 循环或者 for 循环。“自旋”就是自己在这里不停地循环,直到目标达成。而不像普通的锁那样,如果获取不到锁就进入阻塞

  • 自旋锁(spinlock):是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。

  • 自旋锁,它并不会放弃 CPU 时间片,而是通过自旋等待锁的释放,也就是说,它会不停地再次地尝试获取锁,如果失败就再次尝试,直到成功为止

  • 非自旋锁,非自旋锁和自旋锁是完全不一样的,如果它发现此时获取不到锁,它就把自己的线程切换状态,让线程休眠,然后 CPU 就可以在这段时间去做很多其他的事情,直到之前持有这把锁的线程释放了锁,于是 CPU 再把之前的线程恢复回来,让这个线程再去尝试获取这把锁。如果再次失败,就再次让线程休眠

非自旋锁和自旋锁最大的区别,就是如果它遇到拿不到锁的情况,它会把线程阻塞,直到被唤醒。而自旋锁会不停地尝试

二、使用自旋锁

#include <iostream>
#include<thread>
#include<mutex>
#include <atomic>

using namespace std;

class spinlock_mutex
{
public:
	spinlock_mutex() {};

	//spinlock_mutex(const spinlock_mutex& origin); // add this line
	~spinlock_mutex() {};

	void lock()
	{
		while (flag.test_and_set(memory_order_acquire));
	}
	void unlock()
	{
		flag.clear(memory_order_release);
	}
private:
	atomic_flag flag = ATOMIC_FLAG_INIT;
};

int g = 0;
spinlock_mutex mtx;

void task()
{
	for (int i = 0; i < 5; i++)
	{
		lock_guard<spinlock_mutex> my_lock(mtx);
		//mtx.lock();
		g++;
		cout << "task:g="<<g << endl;
		//mtx.unlock();
	}
}

int main()
{
	//cout << "g"<<g << endl;

	thread t1(task);
	thread t2(task);

	t1.join();
	t2.join();
	return 0;
}

输出

三、不使用自旋锁

#include <iostream>
#include<thread>
#include<mutex>
#include <atomic>

using namespace std;

//class spinlock_mutex
//{
//public:
//	spinlock_mutex() 	
//	{
//	}
//
//	//spinlock_mutex(const spinlock_mutex& origin); // add this line
//	~spinlock_mutex() {};
//
//	void lock()
//	{
//		while (flag.test_and_set(memory_order_acquire));
//	}
//	void unlock()
//	{
//		flag.clear(memory_order_release);
//	}
//private:
//	atomic_flag flag = ATOMIC_FLAG_INIT;
//};

int g = 0;
//spinlock_mutex mtx;

void task()
{
	for (int i = 0; i < 5; i++)
	{
		// lock_guard<spinlock_mutex> my_lock(mtx);
		//mtx.lock();
		g++;
		cout << "task:g="<<g << endl;

		//mtx.unlock();
	}
}

int main()
{
	//cout << "g"<<g << endl;

	thread t1(task);
	thread t2(task);

	t1.join();
	t2.join();
	return 0;
}

输出

四、分析

  • 因为io是进程内共享的,所以当我们操作而不加锁时,会出现输出串行的现象。(说明我们实现的自旋锁可用、有效)
  • 我们实现的自旋锁可以配合lock_guard使用;
  • 当然我们也可以直接使用mtx.lock()、mtx.unlock();进行加锁和解锁。
 
posted on   DoubleLi  阅读(340)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
历史上的今天:
2014-03-16 source code analyzer 功能强大的C/C++源代码分析软件 Celerity CRACK 破解版
2014-03-16 分析函数调用关系图(call graph)的几种方法
2014-03-16 用CodeViz绘制函数调用关系图(call graph)
2014-03-16 C++的辅助工具介绍
2013-03-16 位运算中的异或运算 .
2013-03-16 按位与、或、异或等运算方法
2012-03-16 Javascript 对象用法
点击右上角即可分享
微信分享提示