多线程死锁
转载自 [http://www.cocoachina.com/ios/20161129/18216.html]
死锁产生的条件:
互斥条件:一个资源每次只能被一个进程使用。
请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解决死锁的四个方式.
忽略该问题。例如鸵鸟算法,该算法可以应用在极少发生死锁的的情况下。为什么叫鸵鸟算法呢,(鸵鸟策略)
检测死锁并且恢复。(检测与解除策略)
仔细地对资源进行动态分配,以避免死锁。(避免策略)
通过破除死锁四个必要条件之一,来防止死锁产生。(预防策略)
进程(Process)和线程(Thread的区别
进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。拥有独立的内存单元。线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。但是不能独立运行,必须依存在应用程序中,由应用程序提供多个线程执行控制。线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一个进程中的多个线程之间可以并发执行。
进程与应用程序的区别在于应用程序作为一个静态文件存储在计算机系统的硬盘等存储空间中,而进程则是处于动态条件下由操作系统维护的系统资源管理实体。
进程的状态转换图,及导致转换的事件.
三个状态:
1)就绪状态 进程已获得除处理机外的所需资源,等待分配处理机资源,只要分配到CPU就可执行。在某一时刻,可能有若干个进程处于该状态。
2)运行状态 占用处理机资源运行,处于此状态的进程的数目小于等于CPU的数目。
3)阻塞状态 由于进程等待某种条件(如I/O操作或进程同步),在条件满足之前无法继续执行。该事件发生前即使把处理机分配给该进程,也无法运行。
线程状态
锁的分类
互斥锁(mutexlock):
最常使用于线程同步的锁;标记用来保证在任一时刻,只能有一个线程访问该对象,同一线程多次加锁操作会造成死锁;临界区和互斥量都可用来实现此锁,通常情况下锁操作失败会将该线程睡眠等待锁释放时被唤醒
NSLock
NSLock实现了最基本的互斥锁,遵循了 NSLocking 协议,通过 lock 和 unlock 来进行锁定和解锁。其使用也非常简单
- (void)doSomething {
[self.lock lock];
//TODO: do your stuff
[self.lock unlock];
}
NSConditionLock
NSConditionLock 对象所定义的互斥锁可以在使得在某个条件下进行锁定和解锁。它和 NSCondition 很像,但实现方式是不同的。
当两个线程需要特定顺序执行的时候,例如生产者消费者模型,则可以使用 NSConditionLock 。当生产者执行执行的时候,消费者可以通过特定的条件获得锁,当生产者完成执行的时候,它将解锁该锁,然后把锁的条件设置成唤醒消费者线程的条件。锁定和解锁的调用可以随意组合,lock 和 unlockWithCondition: 配合使用 lockWhenCondition: 和 unlock 配合使用。
- (void)producer {
while (YES) {
[self.conditionLock lock];
NSLog(@"have something");
self.count++;
[self.conditionLock unlockWithCondition:1];
}
}
- (void)consumer {
while (YES) {
[self.conditionLock lockWhenCondition:1];
NSLog(@"use something");
self.count--;
[self.conditionLock unlockWithCondition:0];
}
}
pthread_mutex
POSIX 互斥锁是一种超级易用的互斥锁,使用的时候,只需要初始化一个 pthread_mutex_t 用 pthread_mutex_lock 来锁定 pthread_mutex_unlock 来解锁,当使用完成后,记得调用 pthread_mutex_destroy 来销毁锁。
pthread_mutex_init(&lock,NULL);
pthread_mutex_lock(&lock);
//do your stuff
pthread_mutex_unlock(&lock);
pthread_mutex_destroy(&lock);
@synchronized
一个便捷的创建互斥锁的方式,它做了其他互斥锁所做的所有的事情。
- (void)myMethod:(id)anObj
{
@synchronized(anObj)
{
// Everything between the braces is protected by the @synchronized directive.
}
}
自旋锁(spinlock):
同样用来标记只能有一个线程访问该对象,在同一线程多次加锁操作会造成死锁;使用硬件提供的swap指令或test_and_set指令实现;同互斥锁不同的是在锁操作需要等待的时候并不是睡眠等待唤醒,而是循环检测保持者已经释放了锁,这样做的好处是节省了线程从睡眠状态到唤醒之间内核会产生的消耗,在加锁时间短暂的环境下这点会提高很大效率
OSSpinLock
自旋锁,和互斥锁类似,都是为了保证线程安全的锁。但二者的区别是不一样的,对于互斥锁,当一个线程获得这个锁之后,其他想要获得此锁的线程将会被阻塞,直到该锁被释放。但自选锁不一样,当一个线程获得锁之后,其他线程将会一直循环在哪里查看是否该锁被释放。所以,此锁比较适用于锁的持有者保存时间较短的情况下。
// 初始化
spinLock = OS_SPINLOCK_INIT;
// 加锁
OSSpinLockLock(&spinLock);
// 解锁
OSSpinLockUnlock(&spinLock);
os_unfair_lock
自旋锁已经不在安全,然后苹果又整出来个 os_unfair_lock_t (╯‵□′)╯︵┻━┻
这个锁解决了优先级反转问题。
os_unfair_lock_t unfairLock;
unfairLock = &(OS_UNFAIR_LOCK_INIT);
os_unfair_lock_lock(unfairLock);
os_unfair_lock_unlock(unfairLock);
读写锁(rwlock):
高级别锁,区分读和写,符合条件时允许多个线程访问对象。处于读锁操作时可以允许其他线程和本线程的读锁, 但不允许写锁, 处于写锁时则任何锁操作都会睡眠等待;常见的操作系统会在写锁等待时屏蔽后续的读锁操作以防写锁被无限孤立而等待,在操作系统不支持情况下可以用引用计数加写优先等待来用互斥锁实现。 读写锁适用于大量读少量写的环境,但由于其特殊的逻辑使得其效率相对普通的互斥锁和自旋锁要慢一个数量级;值得注意的一点是按POSIX标准 在线程申请读锁并未释放前本线程申请写锁是成功的,但运行后的逻辑结果是无法预测
pthread_rwlock
读写锁,在对文件进行操作的时候,写操作是排他的,一旦有多个线程对同一个文件进行写操作,后果不可估量,但读是可以的,多个线程读取时没有问题的。
- 当读写锁被一个线程以读模式占用的时候,写操作的其他线程会被阻塞,读操作的其他线程还可以继续进行。
- 当读写锁被一个线程以写模式占用的时候,写操作的其他线程会被阻塞,读操作的其他线程也被阻塞。
// 初始化
pthread_rwlock_t rwlock = PTHREAD_RWLOCK_INITIALIZER
// 读模式
pthread_rwlock_wrlock(&lock);
// 写模式
pthread_rwlock_rdlock(&lock);
// 读模式或者写模式的解锁
pthread_rwlock_unlock(&lock);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self readBookWithTag:1];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self readBookWithTag:2];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self writeBook:3];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self writeBook:4];
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self readBookWithTag:5];
});
- (void)readBookWithTag:(NSInteger )tag {
pthread_rwlock_rdlock(&rwLock);
NSLog(@"start read ---- %ld",tag);
self.path = [[NSBundle mainBundle] pathForResource:@"1" ofType:@".doc"];
self.contentString = [NSString stringWithContentsOfFile:self.path encoding:NSUTF8StringEncoding error:nil];
NSLog(@"end read ---- %ld",tag);
pthread_rwlock_unlock(&rwLock);
}
- (void)writeBook:(NSInteger)tag {
pthread_rwlock_wrlock(&rwLock);
NSLog(@"start wirte ---- %ld",tag);
[self.contentString writeToFile:self.path atomically:YES encoding:NSUTF8StringEncoding error:nil];
NSLog(@"end wirte ---- %ld",tag);
pthread_rwlock_unlock(&rwLock);
}
start read ---- 1
start read ---- 2
end read ---- 1
end read ---- 2
start wirte ---- 3
end wirte ---- 3
start wirte ---- 4
end wirte ---- 4
start read ---- 5
end read ---- 5
递归锁(recursivelock):
严格上讲递归锁只是互斥锁的一个特例,同样只能有一个线程访问该对象,但允许同一个线程在未释放其拥有的锁时反复对该锁进行加锁操作; windows下的临界区默认是支持递归锁的,而linux下的互斥量则需要设置参数PTHREAD_MUTEX_RECURSIVE_NP,默认则是不支持。
NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
void MyRecursiveFunction(int value)
{
[theLock lock];
if (value != 0)
{
--value;
MyRecursiveFunction(value);
}
[theLock unlock];
}
MyRecursiveFunction(5);
条件锁:
POSIX Conditions
POSIX 条件锁需要互斥锁和条件两项来实现,虽然看起来没什么关系,但在运行时中,互斥锁将会与条件结合起来。线程将被一个互斥和条件结合的信号来唤醒。
首先初始化条件和互斥锁,当 ready_to_go 为 flase 的时候,进入循环,然后线程将会被挂起,直到另一个线程将 ready_to_go 设置为 true 的时候,并且发送信号的时候,该线程会才被唤醒。
pthread_mutex_t mutex;
pthread_cond_t condition;
Boolean ready_to_go = true;
void MyCondInitFunction()
{
pthread_mutex_init(&mutex);
pthread_cond_init(&condition, NULL);
}
void MyWaitOnConditionFunction()
{
// Lock the mutex.
pthread_mutex_lock(&mutex);
// If the predicate is already set, then the while loop is bypassed;
// otherwise, the thread sleeps until the predicate is set.
while(ready_to_go == false)
{
pthread_cond_wait(&condition, &mutex);
}
// Do work. (The mutex should stay locked.)
// Reset the predicate and release the mutex.
ready_to_go = false;
pthread_mutex_unlock(&mutex);
}
void SignalThreadUsingCondition()
{
// At this point, there should be work for the other thread to do.
pthread_mutex_lock(&mutex);
ready_to_go = true;
// Signal the other thread to begin work.
pthread_cond_signal(&condition);
pthread_mutex_unlock(&mutex);
}
信号量机制实现锁
dispatch_semaphore
信号量机制实现锁,等待信号,和发送信号,正如前边所说的看门人一样,当有多个线程进行访问的时候,只要有一个获得了信号,其他线程的就必须等待该信号释放。
- (void)semphone:(NSInteger)tag {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_NOW);
// do your stuff
dispatch_semaphore_signal(semaphore);
}