一种多线程基于计数无锁实现
2013-09-03 10:09
218 查看
本文介绍一种不加锁,不使用原子操作的多线程同步机制。先申明下,该方案为我在实际编程中创造出来的,事先我没有在其中地方看到关于该方案的介绍。
在多线程编程中,我们经常会遇到线程同步问题,这时候加锁就变得必不可少。但是锁的使用会或多或少带来某些性能上的下降。下面先介绍一个多线程编程中经常遇到的问题模型,然后实现一种无锁解决方案。
问题模型:
R:表示某种资源,线程A往R中存放资源,线程B从R中取出资源。
先看看常规解决方法:
线程A往R中存放资源
1.获取锁(此处可能睡眠)。
2.存入资源。
3.修改资源计数。
4.释放锁。
线程B从R中取出资源
1.获取锁(此处可能睡眠)。
2.取出资源。
3.修改资源计数。
4.释放锁。
下面针对该模型实现一种无锁的解决方案:
首先定义一个数组ARRAY存在资源,假设数组的长度为L,然后再定义两个变量READ和WRITE。READ表示读计数,WRITE表示写计数。
该方案的基本思想为:
1.存入资源增加WRITE。
2.读取资源增加READ。
3.WRITE和READ都只增不减。
4.判断ARRAY存在空余空间,WRITE - READ < L。
5.判断ARRAY为空,WRITE = READ 。
6.定位读位置READ%L,定位写位置WRITE%L。
实际操作流程为:
初始化READ和WRITE为0。
线程A往R中存放一个资源
1.判断数组中的资源未满。
2.存放资源到ARRAY[WRITE%L]
3.增加WRITE。
线程B往R中读取一个资源
1.判断数组中的资源不为空。
2.存放资源到ARRAY[READ%L]
3.增加READ。
可能大家已经看出,上面的实现存在一个严重的问题,就是越界的问题,下面讨论解决方案:
1.越界后WRITE - READ需要保证正确。
大家知道无符号数有一个特性,
0x00000000-0xffffffff = 1;
0x00000000-0xfffffffe = 2;
只要把READ和WRITE定义成无符号数,就能保证WRITE - READ在越界后保证正确性。
2.WRITE%L 和 READ%L在越界后的正确性,我们需要保证以下等式成立:
0xffffffff%L = L -1
为了保证以上等式成立,可以将L设成2的n次方,对应32位整数,n的取值范围为0~31. 由于限定L为2的n次方,WRITE%L 和 READ%L可以写成WRITE&(L-1) 和 READ%L&(L-1).
讨论:
该无锁实现对多线程编程常用的模型提出一种无锁实现,但在使用中还需注意一下几点:
1.为防止程序从高速缓存中取值,必须将变量READ和WRITE定义成volatile类型。
2.该方案要求缓冲区的长度为2的n次方,取值可以为1,2,4,8,16,32,64……,大部分时候可以满足应用上的需求。
3.该方案目前只适用于基于数组的缓冲区结构。
4.该方案目前只适一个读者,一个写者的情形,如果存在多个读者,多个写者,需要分别对读者和写者进行加锁,但是使用该方案还是可以减少锁的力度。
下面贴出参考测试代码:
在多线程编程中,我们经常会遇到线程同步问题,这时候加锁就变得必不可少。但是锁的使用会或多或少带来某些性能上的下降。下面先介绍一个多线程编程中经常遇到的问题模型,然后实现一种无锁解决方案。
问题模型:
R:表示某种资源,线程A往R中存放资源,线程B从R中取出资源。
先看看常规解决方法:
线程A往R中存放资源
1.获取锁(此处可能睡眠)。
2.存入资源。
3.修改资源计数。
4.释放锁。
线程B从R中取出资源
1.获取锁(此处可能睡眠)。
2.取出资源。
3.修改资源计数。
4.释放锁。
下面针对该模型实现一种无锁的解决方案:
首先定义一个数组ARRAY存在资源,假设数组的长度为L,然后再定义两个变量READ和WRITE。READ表示读计数,WRITE表示写计数。
该方案的基本思想为:
1.存入资源增加WRITE。
2.读取资源增加READ。
3.WRITE和READ都只增不减。
4.判断ARRAY存在空余空间,WRITE - READ < L。
5.判断ARRAY为空,WRITE = READ 。
6.定位读位置READ%L,定位写位置WRITE%L。
实际操作流程为:
初始化READ和WRITE为0。
线程A往R中存放一个资源
1.判断数组中的资源未满。
2.存放资源到ARRAY[WRITE%L]
3.增加WRITE。
线程B往R中读取一个资源
1.判断数组中的资源不为空。
2.存放资源到ARRAY[READ%L]
3.增加READ。
可能大家已经看出,上面的实现存在一个严重的问题,就是越界的问题,下面讨论解决方案:
1.越界后WRITE - READ需要保证正确。
大家知道无符号数有一个特性,
0x00000000-0xffffffff = 1;
0x00000000-0xfffffffe = 2;
只要把READ和WRITE定义成无符号数,就能保证WRITE - READ在越界后保证正确性。
2.WRITE%L 和 READ%L在越界后的正确性,我们需要保证以下等式成立:
0xffffffff%L = L -1
为了保证以上等式成立,可以将L设成2的n次方,对应32位整数,n的取值范围为0~31. 由于限定L为2的n次方,WRITE%L 和 READ%L可以写成WRITE&(L-1) 和 READ%L&(L-1).
讨论:
该无锁实现对多线程编程常用的模型提出一种无锁实现,但在使用中还需注意一下几点:
1.为防止程序从高速缓存中取值,必须将变量READ和WRITE定义成volatile类型。
2.该方案要求缓冲区的长度为2的n次方,取值可以为1,2,4,8,16,32,64……,大部分时候可以满足应用上的需求。
3.该方案目前只适用于基于数组的缓冲区结构。
4.该方案目前只适一个读者,一个写者的情形,如果存在多个读者,多个写者,需要分别对读者和写者进行加锁,但是使用该方案还是可以减少锁的力度。
下面贴出参考测试代码:
#include "stdafx.h" #include <windows.h> class ZwAsynCount { public: ZwAsynCount(unsigned uSize) //uSize必须为2的n次方 { m_uReadCount = 0; m_uWriteCount = 0; m_uSize = uSize; } int Write() //返回元素位置 -1表示读失败 { int nRet = -1; if (m_uWriteCount - m_uReadCount < m_uSize) { nRet = m_uWriteCount&(m_uSize-1); } return nRet; } void AddWrite(int nCount = 1) { m_uWriteCount += nCount; } int Read() //返回元素位置 -1表示写失败 { int nRet = -1; if (m_uWriteCount - m_uReadCount > 0) { nRet = m_uReadCount&(m_uSize-1); } return nRet; } void AddRead(int nCount = 1) { m_uReadCount+= nCount; } private: unsigned m_uSize; volatile unsigned m_uReadCount; volatile unsigned m_uWriteCount; }; class ZwTestShareBuffer { public: ZwTestShareBuffer():m_asynCount(128) { } BOOL Read() { BOOL bRet = FALSE; int nPos = m_asynCount.Read(); if (nPos >= 0) { printf("read: %d\n",m_data[nPos]); bRet = TRUE; m_asynCount.AddRead(); } return bRet; } BOOL Write(int nData) { BOOL bRet = FALSE; int nPos = m_asynCount.Write(); if (nPos >= 0) { m_data[nPos] = nData; bRet = TRUE; m_asynCount.AddWrite(); } return bRet; } private: ZwAsynCount m_asynCount; int m_data[128]; }; ZwTestShareBuffer TestAsynShareBuffer; DWORD WINAPI WriteProc(LPVOID lpParam) { int nData = 0; while(TRUE) { if (!TestAsynShareBuffer.Write(nData)) { Sleep(1); } else { nData++; } } return 0; } int _tmain(int argc, _TCHAR* argv[]) { ::CreateThread(NULL,0,WriteProc,NULL,0,NULL); while(TRUE) { if (!TestAsynShareBuffer.Read()) { Sleep(1); } } return 0; }
相关文章推荐
- 一种多线程基于计数无锁实现(C#)(转载)
- 一种基于引用计数机制的智能指针实现
- 一种基于引用计数机制的智能指针实现
- 基于spark排序的一种更廉价的实现方案-附基于spark的性能测试
- c++的对象计数问题的多线程实现
- 一种基于Qt的可伸缩的全异步C/S架构服务器实现(五) 单层无中心集群
- 通过多线程为基于 .NET 的应用程序实现响应迅速的用户[改进]
- 一种基于Qt的可伸缩的全异步C/S架构服务器实现(六) 整合各个模块实现功能
- 一种基于Word文档的手写批注实现方法
- 异步与多线程的区别(异步是目的,多线程是实现它的一种方式,异步的优先级有时候比主线程还高)
- 一种基于 桌面截图的 透明窗体的实现1(理论)
- 通过多线程为基于 .NET 的应用程序实现响应迅速的用户
- 基于TCP协议的Socket通讯,实现多线程简单登录
- Winform基于多线程实现每隔1分钟执行一段代码
- 一种基于Qt的可伸缩的全异步C/S架构server实现(一) 综述
- 一种完全基于开源实现的SOA架构
- 数据结构 多线程安全队列基于数组实现
- Java基于多线程的网络通信实现服务器计算正方形面积
- 一种基于浏览器的自动小票机打印实现方案(js版)
- 基于Linux的消息队列及多线程编程实现的聊天室(一)