• 一种多线程变量区域锁的实现方法


    多线程变量区域锁类定义

    类定义中基于boost库unique_lock和shared_lock定义了读锁ReadLock和写锁WriteLock,将读锁和写锁换成关键区(CRITICAL_SECTION)等也是一样的效果。

    变量区域锁主要是基于以下两点:一、变量的作用范围就是锁定的区域范围;二、C++的特性能够保证析构函数能够得到正确执行,这样就能保证锁的释放。

    正是基于这两点,这里设计的变量区域互斥锁类,主要的思想是在类对象作用范围内,通过对单个bool型变量的值来判断区域锁是否可用,并对该变量的修改进行加锁保护,该区域互斥锁主要包括三个成员函数:

    类构造函数CVariableLock:用于初始化共享互斥体m_lock,以及将变量区域互斥锁外边bool变量指针赋值给m_lock_flag

    类析构函数~CVariableLock:在成功获取到锁的情况下,在析构函数中需要将外部标志bool变量恢复为false,表示释放掉区域锁

    获取锁状态函数GetLockStatus:在该函数中,主要是获取外部标志bool变量的值,如果为true,表明锁被其他线程占用,循环等待50次,每次延迟10ms,当500ms后外部标志变量还是为true,就获取锁失败;否则获取锁成功,并将外部bool标志变量修改为true,表明区域锁被占用

    上面三个函数中,对外部标志bool变量的操作都是在加锁的状态下操作的。

    变量区域锁的类CVariableLock代码如下:

    //写锁定义
    typedef boost::shared_mutex Lock;
    typedef boost::unique_lock<Lock> WriteLock;
    typedef boost::shared_lock<Lock> ReadLock;
    // 区域互斥锁类定义
    class CVariableLock
    {
    public:
        CVariableLock(Lock *lock, bool *lock_flag, int thread_id=0);
        ~CVariableLock(void);
    public:
        bool GetLockStatus();//获取资源状态,1——可访问,0——不可访问
    private:
        Lock *m_lock;
        bool *m_lock_flag;
        bool m_get_lock_flag;
    int m_thread_id; };
    // 区域互斥锁构造函数 CVariableLock::CVariableLock(Lock *lock, bool *lock_flag) { m_get_lock_flag = false; if(lock){ m_lock = lock; } else{ m_lock = NULL; } if(lock_flag){ m_lock_flag = lock_flag; } else{ m_lock_flag = NULL; }
    m_thread_id = thread_id; }
    // 区域互斥锁析构函数 CVariableLock::~CVariableLock(void) { if(!m_lock || !m_lock_flag){ return; } if(m_get_lock_flag){//只有在获取锁成功情况下才重置变量 WriteLock w_lock(*m_lock); *m_lock_flag = false;

         wchar_t str[20];
         wsprintf(str,L"线程%d释放区域锁!\n", m_thread_id);
         OutputDebugString(str);

        }
    }
    // 区域互斥锁锁状态成员函数
    bool CVariableLock::GetLockStatus(){//获取资源状态,返回结果:1——可访问,0——不可访问
        if(!m_lock  || !m_lock_flag){
             return true;
        }
        bool lock_status = true;
        {
            {
                ReadLock w_lock(*m_lock);
                lock_status = *m_lock_flag;
            }
            int count = 50;
            while(lock_status){
                if(count <= 0){
                    break;
                }
                {
                    ReadLock w_lock(*m_lock);
                    lock_status = *m_lock_flag;
                }
                count--;
                Sleep(10);//延时10ms
            }
        }
        
        bool result = ~(lock_status);
        if(result){//获取锁成功
            {
                WriteLock w_lock(*m_lock);
                lock_status = *m_lock_flag;//再次读取变量值
                if(lock_status){//已被其他线程锁定
                    m_get_lock_flag = false;
                    result = false;
                }
                else{
                    m_get_lock_flag = true;
                    *m_lock_flag = true;
                }
            }
        }
        else{//获取锁失败
            m_get_lock_flag = false;
        }
        return result;
    }

    变量区域锁工作流程图

    多线程访问示例

    如下,首先定义了外部标志变量lock_flag并初始化为false,表明锁未被占用,同时定义共享互斥体rw_lock。

    在Fun1和Fun2中分别定义了局部变量互斥区域锁类对象,这样的话,Fun1函数的作用域和Fun2函数的作用域就是互斥的区域,在多线程访问中,Fun1和Fun2不能同时被两个线程同时访问,这样的话,在Fun1和Fun2中公用的成员变量或者全局变量就能够得到有效的保护,避免多线程同时操作导致的数据不确定或者崩溃。

    现在开启10个线程,前5个线程访问Fun1函数,后5个线程访问Fun2函数,可以通过输出看到线程获取区域锁的具体情况:

     

    bool lock_flag = false;
    Lock rw_lock;
    
    DWORD WINAPI Fun1(LPVOID lpParameter)
    {
        int thread_id = *(int*)lpParameter;
        CVariableLock lock(&rw_lock,&lock_flag, thread_id);
        if(!lock.GetLockStatus()){//获取锁失败
            return -1;
        }
        //获取锁成功,锁定区域1
        wchar_t str[20];
        wsprintf(str,L"线程%d取得区域锁!\n", thread_id);
        OutputDebugString(str);
    }
    DWORD WINAPI Fun2(LPVOID lpParameter)
    {
        int thread_id = *(int*)lpParameter;
        CVariableLock lock(&rw_lock,&lock_flag, thread_id);
        if(!lock.GetLockStatus()){//获取锁失败
            return -1;
        }
        //获取锁成功,锁定区域2
        wchar_t str[20];
        wsprintf(str,L"线程%d取得区域锁!\n", thread_id);
        OutputDebugString(str);
    }
    int main(){
        HANDLE threadHandle[10];
        int thread_index[10]={0};
        for(int i = 0; i < 5; i++){
            thread_index[i]=i;
            threadHandle[i] = CreateThread(NULL,0, Fun1, thread_index+i, 0,NULL);
        }
        for(int j = 5; j < 10; j++){
            thread_index[j]=j;
            threadHandle[j] = CreateThread(NULL,0, Fun2, thread_index+j, 0,NULL);
        }
        // 等待线程结束
        WaitForMultipleObjects ( 10, threadHandle, TRUE, INFINITE ) ;
        OutputDebugString(L"所有线程都已经结束!\n");
    
        for ( int i = 0; i < 10; i++ )
            CloseHandle ( threadHandle[i] ) ;
        return 0;
    }

    输出结果为:

    可以看到,没有任意两个线程是同时访问Fun1和Fun2函数的,说明Fun1和Fun2的区域锁确实生效了,同时只有一个线程访问两个函数共同保护的区域。

  • 相关阅读:
    VCL消息处理机制
    效率由心生,快速提高工作效率秘诀
    我的插件架构
    Windows消息机制要点(转)
    SQLite区分大小写查询
    请教: 事件和消息的联系?
    DataAdapter数据集DataSet和数据库的同步(3):使用CommandBuilder来更新数据集
    TCP编程(4): 发送电子邮件 MailMessage, SmtpClient, NetworkCredential
    强类型的数据集,在处理以直接附加方式的SQLServer的MDF文件时无法更新数据原因分析
    javascript调试测试,利用vs2008:智能对象类型感知,方法及属性提示;立即窗口调试等
  • 原文地址:https://www.cnblogs.com/chenyangchun/p/6892862.html
Copyright © 2020-2023  润新知