互斥锁设计,有效的避免死锁

下面一段摘自网络,我觉得这是非常好的。锁是理解非常有帮助。

“为什么要加锁?加锁是为了防止不同的线程訪问同一共享资源造成混乱。

打个例如:人是不同的线程,卫生间是共享资源。

你在上洗手间的时候肯定要把门锁上吧。这就是加锁,仅仅要你在里面。这个卫生间就被锁了,仅仅有你出来之后别人才干用。

想象一下假设卫生间的门没有锁会是什么样?

什么是加锁粒度呢?所谓加锁粒度就是你要锁住的范围是多大。

比方你在家上卫生间,你仅仅要锁住卫生间就能够了吧,不须要将整个家都锁起来不让家人进门吧,卫生间就是你的加锁粒度。

如何才算合理的加锁粒度呢?

事实上卫生间并不仅仅是用来上厕所的,还能够洗澡,洗手。这里就涉及到优化加锁粒度的问题。

你在卫生间里洗澡,事实上别人也能够同一时候去里面洗手,仅仅要做到隔离起来就能够。假设马桶,浴缸,洗漱台都是隔开相对独立的,实际上卫生间能够同一时候给三个人使用。当然三个人做的事儿不能一样。这样就细化了加锁粒度,你在洗澡的时候仅仅要关上浴室的门,别人还是能够进去洗手的。

假设当初设计卫生间的时候没有将不同的功能区域划分隔离开。就不能实现卫生间资源的最大化使用。这就是设计架构的重要性。”

从上述知道,有一种情况就是,当你进了卫生间,锁上了门,这时你从窗户逃走了,从而造成卫生间永远被锁住了。

这就是当中一种死锁。

因此能够设想的就是,当我们从卫生间出来的时候(不管正常出来,还是飞出来,...),都能把锁打开。其他人就能进来。

以下的代码就能实现这个功能。

metux.h

#ifndef MUTEX_LOCK_H
#define MUTEX_LOCK_H

#ifndef WIN32
#include <windows.h>
#endif

#ifdef __unix
#include <pthread.h>
#endif // __unix

class Mutex
{
public:
	Mutex();
	~Mutex();

	void Lock();

	void Unlock();

private:
	Mutex(const Mutex&);
	void operator=(const Mutex&);

#ifdef WIN32
	CRITICAL_SECTION m_mutex;
#endif // WIN32

#ifdef __unix
	pthread_mutex_t m_mutex;
#endif // __unix
};

class MutexLock
{
public:
	explicit MutexLock(Mutex *mutex) :m_mutex(mutex)
	{
		m_mutex->Lock();
	};
	~MutexLock()
	{
		m_mutex->Unlock();
	};

private:
	// 不同意复制
	MutexLock(const MutexLock&);
	void operator=(const MutexLock&);

	Mutex *m_mutex;
};

#endif // !MUTEX_LOCK_H

mutex.cpp

#include "mutex.h"

Mutex::Mutex()
{
#ifdef WIN32
	InitializeCriticalSection(&m_mutex);
#endif
#ifdef __unix
	pthread_mutex_init(&m_mutex, NULL);
#endif // __unix
}

Mutex::~Mutex()
{
#ifdef WIN32
	DeleteCriticalSection(&m_mutex);
#endif

#ifdef __unix
	pthread_mutex_destroy(&m_mutex);
#endif // __unix
}

void Mutex::Lock()
{
#ifdef WIN32
	EnterCriticalSection(&m_mutex);
#endif
#ifdef __unix
	pthread_mutex_lock(&m_mutex);
#endif // __unix
}

void Mutex::Unlock()
{
#ifdef WIN32
	LeaveCriticalSection(&m_mutex);
#endif
#ifdef __unix
	pthread_mutex_unlock(&m_mutex);
#endif // __unix
}

測试

Mutex mutex;

void MutexTest()
{
	MutexLock l(&mutex);
	static int  i = 0;
	printf("i = %d\n", i);
	++i;
}

原理就是,当MutexLock生命周期结束时,会调用析构函数,从而能够实现每次从卫生间出来都能够解锁。当然你能够在MutexText加入大括号({})来约束MetexLock的生命同期。从而减小锁的粒度。

这个设计不管是原理还是实现,还是蛮简单的。

前提是你有这方面的经验,才会想到这样的实现方法。

一年之后:

时间:20150611

近期想用C++ 11的里面的std::mutex取代原来须要定义各种系统的mutex,由于这样代码更加简洁。

上述设计是之前看LevelDB源代码学来,认为挺好,于是分享出来。而今天改动代码时候发现事实上能够用宏定义。

比如:

#define MUTEX_LOCK()	m_mutex.lock(); 	{

#define MUTEX_UNLOCK()	}	m_mutex.unlock();

假设,仅仅使用#define MUTEX_LOCK。没有使用MUTEX_UNLOCK,编译的时候肯定会报错。非常明显,没有MUTEX_UNLOCK。括号是不匹配的。曾经的方法是。假设你忘记了写大括号来控制锁的粒度。那么非常可能要到函数结束的时候才会解锁。如今的方法不存在这样的问题。

可是假设在MUTEX_LOCK 与 MUTEX_UNLOCK之间有return、goto等等待,毫无疑问,将是一个僵局。

版权声明:本文博客原创文章。博客,未经同意,不得转载。

时间: 2024-10-12 12:19:28

互斥锁设计,有效的避免死锁的相关文章

一种有效避免死锁的互斥锁设计

下面是摘自网络的一段话,我觉得很好:对认识锁很有帮助. "为什么要加锁?加锁是为了防止不同的线程访问同一共享资源造成混乱. 打个比方:人是不同的线程,卫生间是共享资源. 你在上洗手间的时候肯定要把门锁上吧,这就是加锁,只要你在里面,这个卫生间就被锁了,只有你出来之后别人才能用.想象一下如果卫生间的门没有锁会是什么样? 什么是加锁粒度呢?所谓加锁粒度就是你要锁住的范围是多大. 比如你在家上卫生间,你只要锁住卫生间就可以了吧,不需要将整个家都锁起来不让家人进门吧,卫生间就是你的加锁粒度. 怎样才算合

linux c学习笔记----互斥锁属性

转自:http://lobert.iteye.com/blog/1762844 互斥锁属性 使用互斥锁(互斥)可以使线程按顺序执行.通常,互斥锁通过确保一次只有一个线程执行代码的临界段来同步多个线程.互斥锁还可以保护单线程代码. 要更改缺省的互斥锁属性,可以对属性对象进行声明和初始化.通常,互斥锁属性会设置在应用程序开头的某个位置,以便可以快速查找和轻松修改.表 4–1 列出了用来处理互斥锁属性的函数. 表 4–1 互斥锁属性例程 操作 相关函数说明 初始化互斥锁属性对象 pthread_mut

互斥锁- pthread_mutex

一. 什么是互斥锁 计算机中,当多个进程或者线程共享一个临界区(例如:共享内存空间或者全局变量),如果仅对该临界区读取,不进行内容修改,即使同时多次读取都是没有问题的. 但是,当我们需要修改临界区内的内容时,我们就必须面对一个情况:同时有多个操作对临界区的内容进行修改,在操作完后,保留的是那一次操作的结果?为了解决这个 问题,可以规定对临界区的修改操作,同时最多只能有一个来修改.程序中实现的方法之一就是互斥锁. 互斥锁,具有三个特点: (i)原子性:把一个互斥锁定义为一个原子操作,这意味着操作系

iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等

iOS并发编程笔记,包含GCD,Operation Queues,Run Loops,如何在后台绘制UI,后台I/O处理,最佳安全实践避免互斥锁死锁优先级反转等,以及如何使用GCD监视进程文件文件夹,并发测试的方案等 线程 使用Instruments的CPU strategy view查看代码如何在多核CPU中执行.创建线程可以使用POSIX 线程API,或者NSThread(封装POSIX 线程API).下面是并发4个线程在一百万个数字中找最小值和最大值的pthread例子: #import

解决NSDistributedLock进程互斥锁的死锁问题(一)

在MAC下的多进程开发中,NSDistributedLock是一个非常方便的互斥锁解决方案,一般的使用方法: 12345678 NSDistributedLock *lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/lock.lock"];while (![lock tryLock]){ sleep(1);} //do something[lock unlock]; 但在实际使用过程中,当执行到do

死锁现象与解决方案,开启线程的2种方式,守护线程,线程VS进程,线程互斥锁,信号量

死锁现象与解决方案 from threading import Thread,Lock,active_count import time mutexA=Lock() # 锁1 mutexB=Lock() # 锁2 class Mythread(Thread): def run(self): self.f1() self.f2() def f1(self): mutexA.acquire() print('%s 拿到A锁' %self.name) mutexB.acquire() print('%

Python36 1.joinablequeue 2.线程理论 3.多线程对比多进程 4.线程的使用方式 4.1.产生 线程的两种方式 4.2.守护线程 4.3.线程安全问题 4.3.1.互斥锁 4.3.2.死锁 4.3.3.可重入锁 4.3.4.信号量

复习1.守护进程2.互斥锁(解决数据错乱的方法)3.IPC(进程间通讯)4.生产者与消费者模型 详解:1.守护进程 一个进程可以设为另一个进程的守护进程 特点:被守护的进程结束时,守护进程也会随之结束 本质:父进程交给子进程一个任务,然而父进程 先于子进程结束了,子进程的任务也就没有必要 继续执行了 格式:开始前加 p.daemon=True 2.互斥锁(解决数据错乱的方法)方法一:互斥锁 互斥 互相排斥 锁的本质:一个标志 标志的两个状态: 1.锁定 2.未锁定 什么时候用? 当多个进程要操作

36 线程 队列 守护线程 互斥锁 死锁 可重入锁 信号量

线程 线程是操作系统最小的运算调度单位,被包含在进程中,一个线程就是一个固定的 执行流程 线程和进程的关系 线程不能单独存在 必须存在于进程中, 进程是一个资源单位,其包含了运行程序所需的所有资源 线程才是真正的执行单位 没有线程,进程中的资源无法被利用起来,所以一个进程至少包含一个线程,称之为主线程 当我们启动一个程序时,操作系统就会自己为这个程序创建一个主线程 线程可以由程序后期开启 ,自己开启线程称之为子线程 为什么需要线程 目的只有一个就是提高效率 就像一个车间 如果产量跟不上 就再造一

Python Threading 线程/互斥锁/死锁/GIL锁

导入线程包 import threading 准备函数线程,传参数 t1 = threading.Thread(target=func,args=(args,)) 类继承线程,创建线程对象 class MyThread(threading.Thread) def run(self): pass if __name__ == "__main__": t = MyThread() t.start() 线程共享全面变量,但在共享全局变量时会出现数据错误问题使用 threading 模块中的