一种多线程基于计数无锁实现(C#)(转载)

来源:互联网 发布:java设计模式实现 编辑:程序博客网 时间:2024/06/13 16:53

转自:http://blog.csdn.net/chzuping/article/details/10960061   chzuping的专栏

chzuping的专栏

本文介绍一种不加锁,不使用原子操作的多线程同步机制。先申明下,该方案为我在实际编程中创造出来的,事先我没有在其中地方看到关于该方案的介绍。

     在多线程编程中,我们经常会遇到线程同步问题,这时候加锁就变得必不可少。但是锁的使用会或多或少带来某些性能上的下降。下面先介绍一个多线程编程中经常遇到的问题模型,然后实现一种无锁解决方案。


     问题模型:
     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.该方案目前只适一个读者,一个写者的情形,如果存在多个读者,多个写者,需要分别对读者和写者进行加锁,但是使用该方案还是可以减少锁的力度。

 下面贴出参考测试代码:

[cpp] view plaincopy
  1. #include "stdafx.h"  
  2. #include <windows.h>  
  3.   
  4. class ZwAsynCount  
  5. {  
  6. public:  
  7.     ZwAsynCount(unsigned uSize) //uSize必须为2的n次方  
  8.     {  
  9.         m_uReadCount = 0;  
  10.         m_uWriteCount = 0;  
  11.         m_uSize = uSize;  
  12.     }  
  13.     int Write()                 //返回元素位置 -1表示读失败  
  14.     {  
  15.         int nRet = -1;  
  16.   
  17.         if (m_uWriteCount - m_uReadCount < m_uSize)  
  18.         {  
  19.             nRet = m_uWriteCount&(m_uSize-1);  
  20.               
  21.         }  
  22.         return nRet;  
  23.     }  
  24.     void AddWrite(int nCount = 1)  
  25.     {  
  26.         m_uWriteCount += nCount;  
  27.     }  
  28.     int Read()                  //返回元素位置 -1表示写失败  
  29.     {  
  30.         int nRet = -1;  
  31.   
  32.         if (m_uWriteCount - m_uReadCount > 0)  
  33.         {  
  34.             nRet = m_uReadCount&(m_uSize-1);  
  35.         }  
  36.   
  37.         return nRet;  
  38.     }  
  39.     void AddRead(int nCount = 1)  
  40.     {  
  41.         m_uReadCount+= nCount;  
  42.     }  
  43. private:  
  44.     unsigned m_uSize;  
  45.     volatile unsigned m_uReadCount;  
  46.     volatile unsigned m_uWriteCount;  
  47. };  
  48.   
  49. class ZwTestShareBuffer  
  50. {  
  51. public:  
  52.     ZwTestShareBuffer():m_asynCount(128)  
  53.     {  
  54.   
  55.     }  
  56.     BOOL Read()  
  57.     {  
  58.         BOOL bRet = FALSE;  
  59.         int nPos = m_asynCount.Read();  
  60.         if (nPos >= 0)  
  61.         {  
  62.             printf("read: %d\n",m_data[nPos]);  
  63.             bRet = TRUE;  
  64.             m_asynCount.AddRead();  
  65.         }  
  66.         return bRet;  
  67.     }  
  68.     BOOL Write(int nData)  
  69.     {  
  70.         BOOL bRet = FALSE;  
  71.         int nPos = m_asynCount.Write();  
  72.         if (nPos >= 0)  
  73.         {  
  74.             m_data[nPos] = nData;  
  75.             bRet = TRUE;  
  76.             m_asynCount.AddWrite();  
  77.         }  
  78.         return bRet;  
  79.     }  
  80. private:  
  81.     ZwAsynCount m_asynCount;  
  82.     int m_data[128];  
  83. };  
  84.   
  85.   
  86. ZwTestShareBuffer TestAsynShareBuffer;  
  87.   
  88.   
  89. DWORD  WINAPI  WriteProc(LPVOID lpParam)  
  90. {  
  91.     int nData = 0;   
  92.     while(TRUE)  
  93.     {  
  94.         if (!TestAsynShareBuffer.Write(nData))  
  95.         {  
  96.             Sleep(1);  
  97.         }  
  98.         else  
  99.         {  
  100.             nData++;  
  101.         }  
  102.     }  
  103.     return 0;  
  104. }  
  105.   
  106. int _tmain(int argc, _TCHAR* argv[])  
  107. {  
  108.     ::CreateThread(NULL,0,WriteProc,NULL,0,NULL);  
  109.   
  110.     while(TRUE)  
  111.     {  
  112.         if (!TestAsynShareBuffer.Read())  
  113.         {  
  114.             Sleep(1);  
  115.         }  
  116.     }  
  117.     return 0;  
  118. }  
0 0
原创粉丝点击