Objective-C 关于锁的种类

Objective-C中不同方式实现锁(一)

转载http://www.tanhao.me/pieces/616.html/

为什么需要使用锁,当然熟悉多线程的你,自然不会对它觉得陌生。

那你在代码中是否很好的使用了锁的机制呢?你又知道几种实现锁的方法呢?

今天一起来探讨一下Objective-C中几种不同方式实现的锁,在这之前我们先构建一个测试用的类,假想它是我们的一个共享资源,method1与method2是互斥的,代码如下:

12345678910111213
@implementation TestObj

- (void)method1{    NSLog(@"%@",NSStringFromSelector(_cmd));}

- (void)method2{    NSLog(@"%@",NSStringFromSelector(_cmd));    }

@end

1.使用NSLock实现的锁

12345678910111213141516171819
//主线程中TestObj *obj = [[TestObj alloc] init];NSLock *lock = [[NSLock alloc] init];

//线程1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [lock lock];    [obj method1];    sleep(10);    [lock unlock];});

//线程2dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);//以保证让线程2的代码后执行    [lock lock];    [obj method2];    [lock unlock];});

看到打印的结果了吗,你会看到线程1锁住之后,线程2会一直等待走到线程1将锁置为unlock后,才会执行method2方法。

NSLock是Cocoa提供给我们最基本的锁对象,这也是我们经常所使用的,除lock和unlock方法外,NSLock还提供了tryLock和lockBeforeDate:两个方法,前一个方法会尝试加锁,如果锁不可用(已经被锁住),刚并不会阻塞线程,并返回NO。lockBeforeDate:方法会在所指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。

2.使用synchronized关键字构建的锁

当然在Objective-C中你还可以用@synchronized指令快速的实现锁:

123456789101112131415161718
//主线程中TestObj *obj = [[TestObj alloc] init];

//线程1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    @synchronized(obj){        [obj method1];        sleep(10);    }});

//线程2dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    @synchronized(obj){        [obj method2];    }});

@synchronized指令使用的obj为该锁的唯一标识,只有当标识相同时,才为满足互斥,如果线程2中的@synchronized(obj)改为@synchronized(other),刚线程2就不会被阻塞,@synchronized指令实现锁的优点就是我们不需要在代码中显式的创建锁对象,便可以实现锁的机制,但作为一种预防措施,@synchronized块会隐式的添加一个异常处理例程来保护代码,该处理例程会在异常抛出的时候自动的释放互斥锁。所以如果不想让隐式的异常处理例程带来额外的开销,你可以考虑使用锁对象。

3.使用C语言的pthread_mutex_t实现的锁

123456789101112131415161718192021
//主线程中TestObj *obj = [[TestObj alloc] init];

__block pthread_mutex_t mutex;pthread_mutex_init(&mutex, NULL);

//线程1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    pthread_mutex_lock(&mutex);    [obj method1];    sleep(5);    pthread_mutex_unlock(&mutex);});

//线程2dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    pthread_mutex_lock(&mutex);    [obj method2];    pthread_mutex_unlock(&mutex);});

pthread_mutex_t定义在pthread.h,所以记得#include 
4.使用GCD来实现的”锁”
以上代码构建多线程我们就已经用到了GCD的dispatch_async方法,其实在GCD中也已经提供了一种信号机制,使用它我们也可以来构建一把”锁”(从本质意义上讲,信号量与锁是有区别,具体差异参加信号量与互斥锁之间的区别):

12345678910111213141516171819
//主线程中TestObj *obj = [[TestObj alloc] init];dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);

//线程1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);    [obj method1];    sleep(10);    dispatch_semaphore_signal(semaphore);});

//线程2dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);    [obj method2];    dispatch_semaphore_signal(semaphore);});

至于代码产生的效果当然和上一例是一模一样的,关于信号机制,熟悉C编程的你肯定也不会陌生的,关于GCD中更多关于dispatch_semaphore_t的信息,可以跳转到本博客的这一往篇文章:GCD介绍(三): Dispatch Sources

好了,以上就是我所列举了几种方式来实现锁,当然锁大多数情况下也是配合多线程一起使用的,关于多线程编程,我这儿就不赘述了。

Objective-C中不同方式实现锁(二)

在上一文中,我们已经讨论过用Objective-C锁几种实现(跳转地址),也用代码实际的演示了如何通过构建一个互斥锁来实现多线程的资源共享及线程安全,今天我们继续讨论锁的一些高级用法。

1.NSRecursiveLock递归锁

平时我们在代码中使用锁的时候,最容易犯的一个错误就是造成死锁,而容易造成死锁的一种情形就是在递归或循环中,如下代码:

123456789101112131415161718192021222324252627282930
//主线程中NSLock *theLock = [[NSLock alloc] init];TestObj *obj = [[TestObj alloc] init];

//线程1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

    static void(^TestMethod)(int);    TestMethod = ^(int value)    {        [theLock lock];        if (value > 0)        {            [obj method1];            sleep(5);            TestMethod(value-1);        }        [theLock unlock];    };

    TestMethod(5);});

//线程2dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    sleep(1);    [theLock lock];    [obj method2];    [theLock unlock];});

以上的代码中,就是一种典型的死锁情况,因为在线程1中的递归block中,锁会被多次的lock,所以自己也被阻塞了,由于以上的代码非常的简短,所以很容易能识别死锁,但在较为复杂的代码中,就不那么容易发现了,那么如何在递归或循环中正确的使用锁呢?此处的theLock如果换用NSRecursiveLock对象,问题便得到解决了,NSRecursiveLock类定义的锁可以在同一线程多次lock,而不会造成死锁。递归锁会跟踪它被多少次lock。每次成功的lock都必须平衡调用unlock操作。只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。

2.NSConditionLock条件锁

当我们在使用多线程的时候,有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁,而不在乎用什么钥匙才能开锁,而我们在处理资源共享的时候,多数情况是只有满足一定条件的情况下才能打开这把锁:

1234567891011121314151617181920
//主线程中NSConditionLock *theLock = [[NSConditionLock alloc] init];

//线程1dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    for (int i=0;i<=2;i++)    {        [theLock lock];        NSLog(@"thread1:%d",i);        sleep(2);        [theLock unlockWithCondition:i];    }});

//线程2dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    [theLock lockWhenCondition:2];    NSLog(@"thread2");    [theLock unlock];});

在线程1中的加锁使用了lock,所以是不需要条件的,所以顺利的就锁住了,但在unlock的使用了一个整型的条件,它可以开启其它线程中正在等待这把钥匙的临界地,而线程2则需要一把被标识为2的钥匙,所以当线程1循环到最后一次的时候,才最终打开了线程2中的阻塞。但即便如此,NSConditionLock也跟其它的锁一样,是需要lock与unlock对应的,只是lock,lockWhenCondition:与unlock,unlockWithCondition:是可以随意组合的,当然这是与你的需求相关的。

//初始化一个NSConditionLock对象
- (id)initWithCondition:(NSInteger)condition

//返回一个Condition
- (NSInteger)condition

//获取和释放锁
1、– (BOOL)lockBeforeDate:(NSDate *)limit
//在指定时间前尝试获取锁,若成功则返回YES 否则返回NO
2、– (void)lockWhenCondition:(NSInteger)condition

//尝试获取锁。在加锁成功前接收对象的Condition必须与参数Condition 相同

3、– (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit
//同上,只是又加上了一个时间

4、– (BOOL)tryLock  //尝试着获取锁
5、– (BOOL)tryLockWhenCondition:(NSInteger)condition
//如果接收对象的condition与给定的condition相等,则尝试获取锁.和lockWhenCondition区别是tryLockWhenCondition直接返回当前状态,没有阻塞线程。
6、– (void)unlockWithCondition:(NSInteger)condition
 //解锁并设置接收对象的condition

3.NSDistributedLock分布式锁

以上所有的锁都是在解决多线程之间的冲突,但如果遇上多个进程或多个程序之间需要构建互斥的情景该怎么办呢?这个时候我们就需要使用到NSDistributedLock了,从它的类名就知道这是一个分布式的Lock,NSDistributedLock的实现是通过文件系统的,所以使用它才可以有效的实现不同进程之间的互斥,但NSDistributedLock并非继承于NSLock,它没有lock方法,它只实现了tryLock,unlock,breakLock,所以如果需要lock的话,你就必须自己实现一个tryLock的轮询,下面通过代码简单的演示一下吧:

程序A:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

1234567
    lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/earning__"];    [lock breakLock];    [lock tryLock];    sleep(10);    [lock unlock];    NSLog(@"appA: OK");});

程序B:

12345678910
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{        lock = [[NSDistributedLock alloc] initWithPath:@"/Users/mac/Desktop/earning__"];

        while (![lock tryLock]) {            NSLog(@"appB: waiting");            sleep(1);        }        [lock unlock];        NSLog(@"appB: OK");    });

先运行程序A,然后立即运行程序B,根据打印你可以清楚的发现,当程序A刚运行的时候,程序B一直处于等待中,当大概10秒过后,程序B便打印出了appB:OK的输出,以上便实现了两上不同程序之间的互斥。/Users/mac/Desktop/earning__是一个文件或文件夹的地址,如果该文件或文件夹不存在,那么在tryLock返回YES时,会自动创建该文件/文件夹。在结束的时候该文件/文件夹会被清除,所以在选择的该路径的时候,应该选择一个不存在的路径,以防止误删了文件。

NSCondition的用法

使用NSCondition,实现多线程的同步,即,可实现生产者消费者问题。

基本思路是,首先要创建公用的NSCondition实例。然后:

  • 消费者取得锁,取产品,如果没有,则wait,这时会释放锁,直到有线程唤醒它去消费产品;
  • 生产者制造产品,首先也是要取得锁,然后生产,再发signal,这样可唤醒wait的消费者。

- (IBAction)conditionTest:(id)sender

{

    NSLog(@"begin condition works!");

    products = [[NSMutableArray alloc] init];

    condition = [[NSCondition alloc] init];

    

    [NSThread detachNewThreadSelector:@selector(createProducter) toTarget:self withObject:nil];

    [NSThread detachNewThreadSelector:@selector(createConsumenr) toTarget:self withObject:nil];

}

- (void)createConsumenr

{

    [condition lock];

    while ([products count] == 0) {

        NSLog(@"wait for products");

        [condition wait];

    }

    [products removeObjectAtIndex:0];

    NSLog(@"comsume a product");

    [condition unlock];

}

- (void)createProducter

{

    [condition lock];

    [products addObject:[[NSObject alloc] init]];

    NSLog(@"produce a product");

    [condition signal];

    [condition unlock];

}

时间: 2024-11-13 04:03:24

Objective-C 关于锁的种类的相关文章

java锁的种类以及辨析(转载)

java锁的种类以及辨析(一):自旋锁 锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及.本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解惑. 1.自旋锁 自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区.如下 01 public class SpinLock { 02  

锁的种类,阻塞,死锁产生与解决办法。

TM锁的种类: TM锁几种模式的互斥关系: 阻塞 定义:当一个会话保持另一个会话正在请求的资源上的锁定时,就会发生阻塞.被阻塞的会话将一直挂起,直到持有锁的会话放弃锁定的资源为止.4个常见的dml语句会产生阻塞INSERTUPDATEDELETESELECT-FOR UPDATE -------------------------------------------------------------- update 的阻塞     试验: 1.    获得会话sid SQL> select s

(转)SQL SERVER的锁机制(一)——概述(锁的种类与范围)

锁定:通俗的讲就是加锁.锁定是 Microsoft SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制. 定义:当有事务操作时,数据库引擎会要求不同类型的锁定,如相关数据行.数据页或是整个数据表,当锁定运行时,会阻止其他事务对已经锁定的数据行.数据页或数据表进行操作.只有在当前事务对于自己锁定的资源不在需要时,才会释放其锁定的资源,供其他事务使用. 一.锁的种类与范围(如下表) 锁类型 说明 共享 (S) 用于不更改或不更新数据的读取操作,如 SELECT 语句.

SQL SERVER的锁机制(一)——概述(锁的种类与范围)

锁定:通俗的讲就是加锁.锁定是 Microsoft SQL Server 数据库引擎用来同步多个用户同时对同一个数据块的访问的一种机制. 定义:当有事务操作时,数据库引擎会要求不同类型的锁定,如相关数据行.数据页或是整个数据表,当锁定运行时,会阻止其他事务对已经锁定的数据行.数据页或数据表进行操作.只有在当前事务对于自己锁定的资源不在需要时,才会释放其锁定的资源,供其他事务使用. 一.锁的种类与范围(如下表) 锁类型 说明 共享 (S) 用于不更改或不更新数据的读取操作,如 SELECT 语句.

锁的种类

锁的种类 Java中锁的种类大致分为偏向锁,自旋锁,轻量级锁,重量级锁. 锁的使用方式为:先提供偏向锁,如果不满足的时候,升级为轻量级锁,再不满足,升级为重量级锁.自旋锁是一个过渡的锁状态,不是一种实际的锁类型. 锁只能升级,不能降级. 偏向锁 是一种编译解释锁.如果代码中不可能出现多线程并发争抢同一个锁的时候,JVM编译代码,解释执行的时候,会自动的放弃同步信息.消除synchronized的同步代码结果.使用锁标记的形式记录锁状态.在Monitor中有变量ACC_SYNCHRONIZED.当

Java锁的种类以及辨析

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及.本系列文章将分析JAVA中常见的锁以及其特性,为大家答疑解惑. 1.自旋锁 2.自旋锁的其他种类 3.阻塞锁 4.可重入锁 5.读写锁 6.互斥锁 7.悲观锁 8.乐观锁 9.公平锁 10.非公平锁 11.偏向锁 12.对象锁 13.线程锁 14.锁粗化 15.轻量级锁 16.锁消除

Java锁的种类

1.自旋锁 自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区.如下 public class SpinLock { private AtomicReference<Thread> sign =new AtomicReference<>(); public void lock(){ Thread current = Thread.currentThread(); while(!sign .compareAndSet(null, curr

多线程编程-- part5 锁的种类以及辨析

java中的锁,可以分为同步锁和JUC包中的锁. 同步锁 通过synchronized关键字进行同步,实现对竞争资源的互斥访问的锁,. 原理:对于每一个对象,有且只有一个同步锁,在同一时间点,所有的线程中有且只有一个能获取该同步锁,获取到同步锁的就可以被CPU进行调度,其他线程必须等待,直到获取到同步锁之后才能继续运行,这就是多线程通过同步锁进行同步的原理. JUC包中的锁:java.util.concurrent 功能更强大,使用当然更复杂. JUC包中锁,包括:lock接口,readWrit

java锁的种类以及辨析(一):自旋锁

锁作为并发共享数据,保证一致性的工具,在JAVA平台有多种实现(如 synchronized 和 ReentrantLock等等 ) .这些已经写好提供的锁为我们开发提供了便利,但是锁的具体性质以及类型却很少被提及.本系列文章将分析JAVA下常见的锁名称以及特性,为大家答疑解 惑. 1.自旋锁 自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区.如下 01 public class SpinLock { 02   03   private Atomi