• CRITICAL_SECTION同步易出错的地方


    众所周知通过CRITICAL_SECTION可以对多个线程同步,然而加锁和解锁的次数不匹配将导致死锁:

    1. class CLock  
    2. {  
    3. public:  
    4.     CLock()  
    5.     {  
    6.         InitializeCriticalSection(&m_cs);  
    7.     }  
    8.   
    9.     ~CLock()  
    10.     {  
    11.         DeleteCriticalSection(&m_cs);  
    12.     }  
    13.   
    14.     void Lock()  
    15.     {  
    16.         EnterCriticalSection(&m_cs);  
    17.     }  
    18.   
    19.     void Unlock()  
    20.     {  
    21.         LeaveCriticalSection(&m_cs);  
    22.     }  
    23. private:  
    24.     CRITICAL_SECTION    m_cs;  
    25. };  
    1. int _tmain(int argc, _TCHAR* argv[])  
    2. {  
    3.     unsigned ThreadId1, ThreadId2;  
    4.     HANDLE  hThread1, hThread2;  
    5.     hThread1 = (HANDLE)_beginthreadex(0, 0, ThreadFun, NULL, 0, &ThreadId1);  
    6.     Sleep(1000);  
    7.     hThread2 = (HANDLE)_beginthreadex(0, 0, ThreadFun, NULL, 0, &ThreadId2);  
    8.   
    9.     getchar();  
    10.   
    11.     InterlockedCompareExchange(&gExit, 1, gExit);  
    12.     WaitForSingleObject(hThread1, -1);  
    13.     WaitForSingleObject(hThread2, -1);  
    14.   
    15.     return 0;  
    16. }  


    1、线程A加锁两次、解锁一次,将导致线程B一直不能获得锁:


    1. long gExit = 0;  
    2. CLock gLock;  
    3.   
    4. unsigned int _stdcall ThreadFun(void* argv)  
    5. {  
    6.     while (true)  
    7.     {  
    8.         gLock.Lock();  
    9.         gLock.Lock();  
    10.         if (InterlockedCompareExchange(&gExit, gExit, 0))  
    11.         {  
    12.             return 0;  
    13.             gLock.Unlock();  
    14.         }  
    15.   
    16.         printf("Thread(%d) is Running ", GetCurrentThreadId());  
    17.   
    18.         gLock.Unlock();  
    19.         //gLock.Unlock();  
    20.   
    21.         Sleep(100);  
    22.     }  
    23. }  


    2、线程A加锁一次,解锁两次,当线程A下次再试图获得锁,将不能获取的到陷入死等,也将导致死锁,同理线程B。:


    1. unsigned int _stdcall ThreadFun(void* argv)  
    2. {  
    3.     while (true)  
    4.     {  
    5.         // 加锁一次  
    6.         gLock.Lock();  
    7.         //gLock.Lock();  
    8.         if (InterlockedCompareExchange(&gExit, gExit, 0))  
    9.         {  
    10.             return 0;  
    11.             gLock.Unlock();  
    12.         }  
    13.   
    14.         printf("Thread(%d) is Running ", GetCurrentThreadId());  
    15.   
    16.         // 解锁两次,下次gLock.Lock()将陷入死等  
    17.         gLock.Unlock();  
    18.         gLock.Unlock();  
    19.   
    20.         Sleep(100);  
    21.     }  
    22. }  


    3、解决,可以定义一个自动获得锁,保证加锁和解锁完全匹配:

    1. class AutoLock  
    2. {  
    3. public:  
    4.     AutoLock()  
    5.     {  
    6.         m_lock.Lock();  
    7.     }  
    8.     ~AutoLock()  
    9.     {  
    10.         m_lock.Unlock();  
    11.     }  
    12.   
    13. private:  
    14.     CLock   m_lock;  
    15.   
    16. private:  
    17.     AutoLock(const AutoLock& lock);  
    18.     AutoLock& operator=(const AutoLock& lock);  
    19. };  
    1. unsigned int _stdcall ThreadFun(void* argv)  
    2. {  
    3.     while (true)  
    4.     {  
    5.         AutoLock auto_lock;  
    6.         if (InterlockedCompareExchange(&gExit, gExit, 0))  
    7.         {  
    8.             return 0;  
    9.         }  
    10.   
    11.         printf("Thread(%d) is Running ", GetCurrentThreadId());  
    12.   
    13.         Sleep(100);  
    14.     }  
    15. }  

    4、后记:以上是最简单的锁实现,加锁和解锁往往导致效率低下,以此改进,使用读写锁、闩锁等不同的加锁粒度来提升性能。
  • 相关阅读:
    每周一坑WAF防护域名主节点异常+阿里远程登录不了
    SmsForwarder转发阿里云登录验证码
    python统计IP段 (基础实现版)
    每周一坑nginx日志写不到elk索引
    阿里云WAF本地验证及误拦截处理
    php 1020
    php 1017
    php 1018
    php 1015
    php 1014
  • 原文地址:https://www.cnblogs.com/hzcya1995/p/13318558.html
Copyright © 2020-2023  润新知