理解线程同步

来源:互联网 发布:程序员常用字体 编辑:程序博客网 时间:2024/05/29 10:33
线程同步是指多线程通过特定的东西(如互斥量)来控制线程之间的执行顺序(同步)
也可以说是在线程之间通过同步建立起执行顺序的关系,如果没有同步那线程之间是各自运行各自的
 
线程同步有三种常用的机制: 互斥量(mutex), 读写锁(rwlock)和条件变量(cond).
互斥量
    互斥量从本质上说就是一把锁, 提供对共享资源的保护访问.
1. 初始化:
    在Linux下, 线程的互斥量数据类型是pthread_mutex_t. 在使用前, 要对它进行初始化:
对于静态分配的互斥量, 可以把它设置为PTHREAD_MUTEX_INITIALIZER, 或者调用pthread_mutex_init.
对于动态分配的互斥量, 在申请内存(malloc)之后, 通过pthread_mutex_init进行初始化, 并且在释放内存(free)前需要调用pthread_mutex_destroy.
原型:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restric attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
头文件: <pthread.h>
返回值: 成功则返回0, 出错则返回错误编号.
说明: 如果使用默认的属性初始化互斥量, 只需把attr设为NULL. 其他值在以后讲解.
2. 互斥操作:
    对共享资源的访问, 要对互斥量进行加锁, 如果互斥量已经上了锁, 调用线程会阻塞, 直到互斥量被解锁. 在完成了对共享资源的访问后, 要对互斥量进行解锁.
首先说一下加锁函数:
头文件: <pthread.h>
原型:
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
返回值: 成功则返回0, 出错则返回错误编号.
说 明: 具体说一下trylock函数, 这个函数是非阻塞调用模式, 也就是说, 如果互斥量没被锁住, trylock函数将把互斥量加锁, 并获得对共享资源的访问权限; 如果互斥量被锁住了, trylock函数将不会阻塞等待而直接返回EBUSY, 表示共享资源处于忙状态.
再说一下解所函数:
头文件: <pthread.h>
原型: int pthread_mutex_unlock(pthread_mutex_t *mutex);
返回值: 成功则返回0, 出错则返回错误编号.
3. 死锁:
    死锁主要发生在有多个依赖锁存在时, 会在一个线程试图以与另一个线程相反顺序锁住互斥量时发生. 如何避免死锁是使用互斥量应该格外注意的东西.
    总体来讲, 有几个不成文的基本原则:
对共享资源操作前一定要获得锁.
完成操作以后一定要释放锁.
尽量短时间地占用锁.
如果有多锁, 如获得顺序是ABC连环扣, 释放顺序也应该是ABC.
线程错误返回时应该释放它所获得的锁.
  1. ifndef __TCMUTEX_H__
  2. #define __TCMUTEX_H__

  3. #if !defined(AFX_MUTEX_H__5B26703E_B4A3_4247_B78C_0A5FD39BD73A__INCLUDED_)
  4. #define AFX_MUTEX_H__5B26703E_B4A3_4247_B78C_0A5FD39BD73A__INCLUDED_

  5. #include <pthread.h>

  6. class TCMutex
  7. {
  8. public:
  9.     TCMutex();
  10.     virtual ~TCMutex();
  11.     /**    @brief    锁*/
  12.     void Lock();
  13.     /**    @brief    解锁*/
  14.     void UnLock();

  15.     bool IsLocked()
  16.     {
  17.         return m_lock;
  18.     }

  19. #ifndef __WIN32__
  20.     pthread_mutex_t* GetMutex()
  21.     {
  22.         return & m_mutex;
  23.     }    
  24. #else
  25.     CRITICAL_SECTION * GetMutex()
  26.     {
  27.         return & m_singlelock;
  28.     }
  29. #endif

  30. private:

  31. #ifndef __WIN32__
  32.     pthread_mutex_t m_mutex;
  33.     pthread_mutexattr_t m_attr;
  34. #else
  35.     CRITICAL_SECTION m_singlelock;
  36. #endif

  37.     bool m_lock;
  38. };
  39. #endif
  1. #ifndef FALSE
  2. #define FALSE 0
  3. #endif

  4. #ifndef TRUE
  5. #define TRUE 1
  6. #endif

  7. /** @file    TCMutex.cpp
  8. *    @brief    互斥类源码文件
  9. *    @version 1.0
  10. *    @date 2008-12-21*/

  11. TCMutex::TCMutex()
  12. {
  13. #ifndef __WIN32__
  14.     pthread_mutexattr_init( & m_attr );
  15. #ifdef __LINUX__
  16.     pthread_mutexattr_settype(& m_attr,PTHREAD_MUTEX_RECURSIVE_NP);
  17. #else
  18.     pthread_mutexattr_settype(& m_attr,PTHREAD_MUTEX_RECURSIVE);    
  19. #endif    
  20.     pthread_mutex_init( & m_mutex, & m_attr);
  21. #else
  22.     InitializeCriticalSection( &m_singlelock );
  23. #endif    
  24.     m_lock = false;
  25. }

  26. TCMutex::~TCMutex()
  27. {
  28. #ifndef __WIN32__
  29.     pthread_mutex_destroy( &m_mutex );
  30. #else
  31.     DeleteCriticalSection( &m_singlelock );
  32. #endif
  33. }


  34. void TCMutex::Lock()
  35. {
  36. #ifndef __WIN32__
  37.     pthread_mutex_lock( &m_mutex );
  38.     //if( pthread_mutex_lock(&m_mutex ) < 0 )
  39.     //{
  40.     //    throw TCException("Lock() exception!");
  41.     //}
  42. #else
  43.     EnterCriticalSection( &m_singlelock );
  44. #endif
  45.     m_lock = true;
  46. }

  47. void TCMutex::UnLock()
  48. {
  49. #ifndef __WIN32__
  50.     pthread_mutex_unlock( &m_mutex );
  51.     //if( pthread_mutex_unlock(&m_mutex ) < 0 )
  52.     //{
  53.     //    throw TCException("UnLock() exception!");
  54.     //}
  55. #else
  56.     LeaveCriticalSection( &m_singlelock );
  57. #endif
  58.     m_lock = false;
  59. }

读写锁
    在线程同步系列的第一篇文章里已经说过, 读写锁是因为有3种状态, 所以可以有更高的并行性.
1. 特性:
    一次只有一个线程可以占有写模式的读写锁, 但是可以有多个线程同时占有读模式的读写锁. 正是因为这个特性,
当读写锁是写加锁状态时, 在这个锁被解锁之前, 所有试图对这个锁加锁的线程都会被阻塞.
当读写锁在读加锁状态时, 所有试图以读模式对它进行加锁的线程都可以得到访问权, 但是如果线程希望以写模式对此锁进行加锁, 它必须阻塞直到所有的线程释放锁.
通常, 当读写锁处于读模式锁住状态时, 如果有另外线程试图以写模式加锁, 读写锁通常会阻塞随后的读模式锁请求, 这样可以避免读模式锁长期占用, 而等待的写模式锁请求长期阻塞.
2. 适用性:
    读写锁适合于对数据结构的读次数比写次数多得多的情况. 因为, 读模式锁定时可以共享, 以写模式锁住时意味着独占, 所以读写锁又叫共享-独占锁.
3. 初始化和销毁:
#include <pthread.h>
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
成功则返回0, 出错则返回错误编号.
同互斥量以上, 在释放读写锁占用的内存之前, 需要先通过pthread_rwlock_destroy对读写锁进行清理工作, 释放由init分配的资源.
4. 读和写:
#include <pthread.h>
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
成功则返回0, 出错则返回错误编号.
这3个函数分别实现获取读锁, 获取写锁和释放锁的操作. 获取锁的两个函数是阻塞操作, 同样, 非阻塞的函数为:
#include <pthread.h>
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
成功则返回0, 出错则返回错误编号.
非阻塞的获取锁操作, 如果可以获取则返回0, 否则返回错误的EBUSY.
    1. ifndef __TCRWLOCK_H__
    2. #define __TCRWLOCK_H__

    3. #if !defined(AFX_MUTEX_H__5B26703E_B4A3_4247_B78C_0A5FD39BD73A__INCLUDED_)
    4. #define AFX_MUTEX_H__5B26703E_B4A3_4247_B78C_0A5FD39BD73A__INCLUDED_

    5. #include <pthread.h>

    6. #ifdef __WIN32__

    7. typedef struct _RWLock
    8. {
    9.     HANDLE    hMutex;
    10.     HANDLE    hDataLock;
    11.     int        nReaderCount;
    12. } RWLock;

    13. #endif

    14. class TCRWLock
    15. {
    16. public:
    17.     TCRWLock();
    18.     ~TCRWLock();

    19.     bool ReadLock(void);//请求读操作锁,请求不成功则阻塞
    20.     bool ReleaseReadLock(void);//释放读操作锁

    21.     bool WriteLock(void);//请求写操作锁,请求不成功则阻塞
    22.     bool ReleaseWriteLock(void);//释放写操作锁

    23. private:
    24. #ifdef __WIN32__
    25.     bool MyWaitForSingleObject(HANDLE hObject);
    26. #else
    27.     bool MyWaitForSingleObject(void * hObject);
    28. #endif
    29.     bool InitRWLock();
    30.     bool DestroyRWLock();
    31. private:

    32. #ifdef __WIN32__
    33.     RWLock    m_RWLock; //读写锁
    34. #else
    35.     pthread_rwlock_t m_RWLock;
    36. #endif

    37. };

    38. typedef class TCMutex CMyMutex;
    39. typedef class TCRWLock CRWLock;


    40. #endif //!defined(AFX_MUTEX_H__5B26703E_B4A3_4247_B78C_0A5FD39BD73A__INCLUDED_)
    1. //读写锁

    2. //完成读写锁初始化
    3. TCRWLock::TCRWLock()
    4. {
    5.     if(!InitRWLock())
    6.     {
    7.         throw TCException("Init RwLock Error!");
    8.     }
    9. }

    10. TCRWLock::~TCRWLock()
    11. {
    12.     DestroyRWLock();
    13. }

    14. #ifdef __WIN32__
    15. bool TCRWLock::MyWaitForSingleObject(HANDLE hObject)
    16. #else
    17. bool TCRWLock::MyWaitForSingleObject(void* hObject)
    18. #endif
    19. {
    20. #ifdef __WIN32__
    21.     DWORD result;
    22.     result = WaitForSingleObject(hObject, INFINITE);

    23.     return (result == WAIT_OBJECT_0);
    24. #else
    25.     return true;
    26. #endif
    27. }

    28. bool TCRWLock::InitRWLock()
    29. {
    30. #ifdef __WIN32__
    31.     m_RWLock.nReaderCount = 0;
    32.     m_RWLock.hDataLock = CreateSemaphore(NULL, 1, 1,NULL);

    33.     if (m_RWLock.hDataLock== NULL)
    34.     {
    35.         return false;
    36.     }

    37.     m_RWLock.hMutex = CreateMutex(NULL,FALSE, NULL);

    38.     if (m_RWLock.hMutex== NULL)
    39.     {
    40.         CloseHandle(m_RWLock.hDataLock);
    41.         return false;
    42.     }

    43.     return true;
    44. #else
    45.     
    46.     if( pthread_rwlock_init(&m_RWLock,NULL) != 0 )
    47.     {
    48.         throw TCException("TCRWLock::InitRWLock() exception!");
    49.     }

    50.     return true;
    51. #endif
    52. }

    53. bool TCRWLock::DestroyRWLock()
    54. {
    55. #ifdef __WIN32__
    56.     DWORD result = WaitForSingleObject(m_RWLock.hDataLock, INFINITE);

    57.     if (result!= WAIT_OBJECT_0)
    58.     {
    59.         return false;
    60.     }

    61.     CloseHandle(m_RWLock.hMutex);
    62.     CloseHandle(m_RWLock.hDataLock);

    63.     return true;
    64. #else
    65.     pthread_rwlock_destroy(&m_RWLock);

    66.     return true;
    67. #endif
    68. }

    69. bool TCRWLock::ReadLock()
    70. {
    71. #ifdef __WIN32__
    72.     bool result = TRUE;

    73.     if (MyWaitForSingleObject(m_RWLock.hMutex)== FALSE)
    74.     {
    75.         return FALSE;
    76.     }

    77.     ++ m_RWLock.nReaderCount;

    78.     if(m_RWLock.nReaderCount== 1)
    79.     {
    80.         result = MyWaitForSingleObject(m_RWLock.hDataLock);
    81.     }

    82.     ReleaseMutex(m_RWLock.hMutex);

    83.     return result;
    84. #else
    85.     if (pthread_rwlock_rdlock(&m_RWLock)!= 0)
    86.     {
    87.         throw TCException("RLock() exception!");
    88.     }

    89.     return true;
    90. #endif
    91. }

    92. bool TCRWLock::ReleaseReadLock()
    93. {
    94. #ifdef __WIN32__
    95.     LONG lPrevCount;
    96.     int result = TRUE;

    97.     if (MyWaitForSingleObject(m_RWLock.hMutex)== FALSE)
    98.     {
    99.         return false;
    100.     }

    101.     --m_RWLock.nReaderCount;

    102.     if (m_RWLock.nReaderCount== 0)
    103.     {
    104.         result = ReleaseSemaphore(m_RWLock.hDataLock, 1,&lPrevCount);
    105.     }

    106.     ReleaseMutex(m_RWLock.hMutex);

    107.     return result;
    108. #else
    109.     if (pthread_rwlock_unlock(&m_RWLock)!= 0)
    110.     {
    111.         throw TCException("TCRWLock::ReleaseReadLock() exception!");
    112.     }

    113.     return true;
    114. #endif
    115. }

    116. bool TCRWLock::WriteLock()
    117. {
    118. #ifdef __WIN32__
    119.     return MyWaitForSingleObject(m_RWLock.hDataLock);
    120. #else
    121.     if (pthread_rwlock_wrlock(&m_RWLock)!= 0)
    122.     {
    123.         throw TCException("TCRWLock::WLock() exception!");
    124.     }
    125.     return true;
    126. #endif
    127. }


    128. bool TCRWLock::ReleaseWriteLock()
    129. {
    130. #ifdef __WIN32__
    131.     int result = TRUE;
    132.     LONG lPrevCount;

    133.     result = ReleaseSemaphore(m_RWLock.hDataLock, 1,&lPrevCount);

    134.     if (lPrevCount!= 0)
    135.     {
    136.         return false;
    137.     }

    138.     return result;
    139. #else
    140.     if (pthread_rwlock_unlock(&m_RWLock)!= 0)
    141.     {
    142.         throw TCException("TCRWLock::ReleaseWriteLock() exception!");
    143.     }

    144.     return true;
    145. #endif
    146. }

    条件变量
         条件变量分为两部分: 条件和变量. 条件本身是由互斥量保护的. 线程在改变条件状态前先要锁住互斥量.
    1. 初始化:
        条件变量采用的数据类型是pthread_cond_t, 在使用之前必须要进行初始化, 这包括两种方式:
    静态: 可以把常量PTHREAD_COND_INITIALIZER给静态分配的条件变量.
    动态: pthread_cond_init函数, 是释放动态条件变量的内存空间之前, 要用pthread_cond_destroy对其进行清理.
    #include <pthread.h>
    int pthread_cond_init(pthread_cond_t *restrict cond, pthread_condattr_t *restrict attr);
    int pthread_cond_destroy(pthread_cond_t *cond);
    成功则返回0, 出错则返回错误编号.
        当pthread_cond_init的attr参数为NULL时, 会创建一个默认属性的条件变量; 非默认情况以后讨论.
    2. 等待条件:
    #include <pthread.h>
    int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);
    int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
    成功则返回0, 出错则返回错误编号.
        这两个函数分别是阻塞等待和超时等待.
        等待条件函数等待条件变为真, 传递给pthread_cond_wait的互斥量对条件进行保护, 调用者把锁住的互斥量传递给函数. 函数把调用线程放到等待条件的线程列表上, 然后对互斥量解锁, 这两个操作是原子的. 这样便关闭了条件检查和线程进入休眠状态等待条件改变这两个操作之间的时间通道, 这样线程就不会错过条件的任何变化.
        当pthread_cond_wait返回时, 互斥量再次被锁住.
    3. 通知条件:
    #include <pthread.h>
    int pthread_cond_signal(pthread_cond_t *cond);
    int pthread_cond_broadcast(pthread_cond_t *cond);
    成功则返回0, 出错则返回错误编号.
        这两个函数用于通知线程条件已经满足. 调用这两个函数, 也称向线程或条件发送信号. 必须注意, 一定要在改变条件状态以后再给线程发送信号.

     
    1. ///
    2. /// @file Lock.h
    3. /// @brief
    4. /// @author guozhiming
    5. /// @date 2007-04-11
    6. ///

    7. #ifndef __LOCK__
    8. #define __LOCK__
    9. #include "def.h"

    10. /// @brief 锁的基本操作 1:读操作的时候读锁可以并发 2:写操作的时候,独占操作
    11. class G_Lock
    12. {
    13.     public:
    14.         
    15.         /// @brief 构造函数初始化互斥锁, 条件变量
    16.         G_Lock();

    17.         /// @brief 析构函数释放互斥锁,和条件变量
    18.         ~G_Lock();

    19.         /// @brief 读锁 readCounter++, 锁住readCounter
    20.         void rLock();

    21.         /// @brief 解锁 readCounter--, 如果readCounter = 0, pthread_cond_signal通知
    22.         void unrLock();

    23.         /// @brief 写锁 如果readCounter不为0一直 pthread_cond_wait等待下去
    24.         void wLock();

    25.         /// @brief 解锁
    26.         void unwLock();
    27.     
    28.     private:
    29.         pthread_mutex_t r_Mutex; ///读锁 ,锁住readCounter

    30.         pthread_mutex_t w_Mutex; ///锁住同步资源
    31.         
    32.         unsigned long readCounter; ///条件变量如果为0通知(pthread_cond_signal)

    33.         pthread_cond_t condReadCounter;
    34. };


    35. #endif
      1. ///
      2. /// @file Lock.cpp
      3. /// @brief
      4. /// @author guozhiming
      5. /// @date 2007-04-11
      6. ///
      7. #include "Lock.h"

      8. G_Lock::G_Lock()
      9. {
      10.     pthread_mutex_init(&r_Mutex, NULL);
      11.     pthread_mutex_init(&w_Mutex, NULL);
      12.     pthread_cond_init(&condReadCounter, NULL);
      13.     readCounter = 0;
      14. }

      15. G_Lock::~G_Lock()
      16. {
      17.     pthread_mutex_destroy(&r_Mutex);
      18.     pthread_mutex_destroy(&w_Mutex);
      19.     pthread_cond_destroy(&condReadCounter);
      20. }

      21. void G_Lock::rLock()
      22. {
      23.     pthread_mutex_lock(&w_Mutex);//防止于写操作冲突
      24.     pthread_mutex_lock(&r_Mutex);//防止readCounter 冲突
      25.     readCounter++;
      26.     pthread_mutex_unlock(&w_Mutex);///写解锁
      27.     pthread_mutex_unlock(&r_Mutex);///读解锁
      28. }

      29. void G_Lock::unrLock()
      30. {
      31.     pthread_mutex_lock(&r_Mutex);///读加锁
      32.     readCounter--;
      33.     if(0== readCounter)
      34.     {
      35.         pthread_cond_signal(&condReadCounter);//如果readCounter为0, 表示已经没有读了,可以写
      36.     }
      37.     pthread_mutex_unlock(&r_Mutex);///读解锁
      38. }

      39. void G_Lock::wLock()
      40. {
      41.     pthread_mutex_lock(&w_Mutex);///写加锁
      42.     pthread_mutex_lock(&r_Mutex);///读加锁
      43.     if(0== readCounter)///防止readCounter= 0而且没有调用过rLock()和unrLock()
      44.     {
      45.         pthread_mutex_unlock(&r_Mutex);///读解锁
      46.         return ;
      47.     }
      48.     pthread_cond_wait(&condReadCounter, &r_Mutex);//等待readCounter= 0 , 等待期间r_Mutex被解锁,有信号再加锁
      49.     pthread_mutex_unlock(&r_Mutex);///读解锁
      50. }

      51. void G_Lock::unwLock()
      52. {
      53.     pthread_mutex_unlock(&w_Mutex);//写解锁
      54. }
    0 0
    原创粉丝点击