muduo库学习之线程同步01——互斥器mutex

东阳的学习笔记

使用原则

互斥器保护了临界区,任何时刻最多只有一个线程在此 mutex 划出的临界区内活动。单独使用 mutex 时,我们主要是为了保护共享数据

下面是使用 mutex 时的一些经验和原则:
主要原则有:

  1. RAII手法封装 mutex 的创建、销毁、加锁、解锁这四个操作。
  2. 只使用非递归的 mutex (即不可重入的 mutex)
  3. 不手动调用 lock() 和 unlock() 函数,一切交给栈上对象 GUARD 的构造析构函数来完成
  4. 在每次构造 GUARD 对象的时候,思考调用栈上已经持有的锁,防止因加锁顺序不同而导致死锁。

次要原则有:

  1. 不使用跨进程的 mutex ,进程间通信只用 TCP sockets。
  2. 加锁解锁在同一个线程,线程 a 不能去 unlock 线程 b 持有的锁(RAII 自动保证
  3. 别忘了解锁(RAII 自动保证
  4. 不重复解锁(RAII 自动保证
  5. 必要的时候可以考虑使用 PTHREAD_MUTEX_ERRORCHECK 来排错

只使用非递归的 mutex

递归锁与非递归锁的唯一区别在与:同一个线程可以重复对 recursive mutex 加锁,但是不能重复对 non-recursive mutex 加锁。

non-recursive mutex 不用考虑一个线程会被自己给锁死,这是一个优点,但是却也带来了问题:典型情况是,你以为拿到一个锁就能修改对象了,没想到外层代码已经拿到了锁。

见下面的示例代码,如果在 doit() 中调用了 post,将会导致:

  1. mutex 是非递归的,造成死锁
  2. mutex 是可递归的,外层函数已经将 foo 锁死了,但在内层函数中却修改了foo, 这将导致外层函数的迭代器失效(偶尔),此时程序会 crash
void post(const Foo& f)
{	
	MutexLockGuard lock(mutex);
	foos.push_back(f);
}

void traverse()
{
	MutexLockGuard lock(mutex);
	for (std::vector<Foo>::const_iterator it = foos.begin();
		it != foos.end(); ++it)
	{
		it->doit();
	}
}

一个死锁的例子

在这里插入图片描述

class Request;
class Inventory
{
 public:

  void add(Request *req)
  {
    muduo::MutexLockGuard lock(mutex_);
    requests_.insert(req);
  }

  void remove(Request* req) // __attribute__ ((noinline))
  {
    muduo::MutexLockGuard lock(mutex_);
    requests_.erase(req);
  }

  void printAll() const;

 private:
  mutable muduo::MutexLock mutex_;
  std::set<Request*> requests_;
};

Inventory g_inventory;
class Request
{
 public:
  ~Request()
  {
    x_ = -1;
    muduo::MutexLockGuard lock(mutex_);
    sleep(1);
    g_inventory.remove(this);
  }

  void process() // __attribute__ ((noinline))
  {
    muduo::MutexLockGuard lock(mutex_);
    printf("process() locked \n");
    g_inventory.add(this);
    // ...
  }

  void print() const __attribute__ ((noinline))
  {
    muduo::MutexLockGuard lock(mutex_);
    // ...
    printf("print Request %p x=%d\n", this, x_);
  }

 private:
  mutable muduo::MutexLock mutex_;
  int x_;
};

void Inventory::printAll() const
{
  muduo::MutexLockGuard lock(mutex_);
  printf("printAll() locked \n");
  sleep(1);
  for (std::set<Request*>::const_iterator it = requests_.begin();
      it != requests_.end();
      ++it)
  {
    (*it)->print();
  }
  printf("Inventory::printAll unlocked\n");
}

void threadFunc()
{
	Request *req = new Request;
	req->process();
	delete req;
}

int main()
{
	muduo::Thread thread(threadFunc);
	thread.start();
	usleep(500 * 1000);  // 为了让另一个线程等在前面第67行的sleep上
	g_inventory.printAll();
	thread.join();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

东阳z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值