c++ 线程同步机制

互斥量

在使用互斥量的时候,最好使用RAII进行封装,使用非递归的互斥量,尽量在一个函数内进行lock、unlock。

常有的互斥量对象,简单的互斥对象std::mutex,带有超时机制的互斥对象std::timed_mutex,一般使用RAII来避免死锁的情况。

std::lock_guard,对象生存期内是不允许手动加锁解锁的。构造时可选是否加锁(不加锁时假定当前线程已经获得锁的所有权),析构时自动释放锁,所有权不可转移。

std::unique_lock,对象生存期可以进行手动加锁解锁。比lock_guard更加灵活。


lock_guard只支持std::lock和std::adopt_lock。

unique_lock则支持:std:lock、std::defer_lock、std::try_lock、std::adopt_lock。

defer_lock 不取得互斥量的所有权;当多个线程都使用同样N个互斥量的时候,必须保证其加锁的顺序是一致的这种情况下使用try_lock更好。

try_lock 则会在没有阻塞的时候取得互斥量的所有权;其变种try_lock_for(duration)和try_lock_until(timepoint)。

adopt_lock  即使互斥量已经被另外的线程加锁,也会夺取互斥量的所有权进而在该线程加锁。



条件变量

条件变量是一个或多个线程等待某个布尔表达式为真,即等待别的线程“唤醒”它。

对于wait()端,为了防止虚假唤醒,必须配合锁一起使用:

1. 必须与mutex 一起使用,该布尔表达式的读写需受此mutex 保护

2. mutex 已上锁的时候才能调用wait()

3. 把判断布尔条件和wait() 放到while 循环中

示例:

MutexLock mutex;
Condition cond(mutex);
std::deque<int> queue;

int dequeue()
{
  MutexLockGuard lock(mutex);
  while (queue.empty()) {  // 必须用循环;必须在判断之后再 wait()
    cond.wait(); // 这一步会原子地 unlock mutex 并进入 blocking,不会与 enqueue 死锁
  }
  assert(!queue.empty());
  int top = queue.front();
  queue.pop_front();
  return top;
}

对于 signal/broadcast 端:

1. 不一定要在mutex 已上锁的情况下调用signal 

2. signal 之前一般要修改布尔表达式

3. 修改布尔表达式通常要用mutex保护

void enqueue(int x)
{
  MutexLockGuard lock(mutex);
  queue.push_back(x);
  cond.notify();
}

实现了一个简单的unbounded BlockingQueue

条件变量是较底层的同步原语,很少直接使用,一般都是用它来实现高层的同步措施,如 BlockingQueue CountDownLatch


临界区

临界区是值一个访问共享资源的代码段。当有一个线程访问了临界区之后,其他线程想要访问临界区时会被挂起,直到进入临界区的线程离开。windows API提供了临界区对象结构体CRITICAL_SECTION,常用API有:

1. 申请一个临界区变量  CRITICAL_SECTION gSection;

2.InitializeCriticalSection(&gSection),初始化临界区,唯一的参数是指向结构体CRITICAL_SECTION的指针变量(LPCRITICAL_SECTION lpCriticalSection)。

3.EnterCriticalSection(&gSection),线程进入已经初始化的临界区,并拥有该临界区的所有权。这是一个阻塞函数,如果线程获得临界区的所有权成功,则该函数将返回,调用线程继续执行,否则该函数将一直等待,这样会造成该函数的调用线程也一直等待。如果不想让调用线程等待(非阻塞),则应该使用TryEnterCriticalSection(&gSection)。

4.LeaveCriticalSection(&gSection),线程离开临界区并释放对该临界区的所有权,以便让其他线程也获得访问该共享资源的机会。一定要在程序不适用临界区时调用该函数释放临界区所有权,否则程序将一直等待造成程序假死。

5.DeleteCriticalSection(&gSection),该函数的作用是删除程序中已经被初始化的临界区。如果函数调用成功,则程序会将内存中的临界区删除,防止出现内存错误。


未完待续。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值