美文网首页
OC--各种线程锁

OC--各种线程锁

作者: 啊哈呵 | 来源:发表于2017-10-20 18:54 被阅读1011次

参考:
正确使用多线程同步锁@synchronized()
iOS中的锁
iOS多线程安全详解
iOS 常见知识点(三):Lock

各种锁

1、 简单常见的:
(1)atomic
(2)@synchronized
2、NS对象形式的:
(1)NSLock
(2)NSConditionLock条件锁
(3)NSRecursiveLock递归锁
(4)NSCondition挂起唤醒
3、GCD的:
(1)dispatch_semaphore
(2)dispatch_barrier_async
4、高级的:
(1)OSSpinLock
(2)os_unfair_lock
(3)pthread_mutex_t

性能对比
atomic

atomic:原子属性,只是为gettet、setter方法加锁
atomic与nonatomic的区别

   // nonatomic修饰,会崩溃
    for (int i=0; i< 10000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.obj_nonatomic = [NSObject new];
        });
    }
    
    // atomic修饰,不崩溃
    for (int i=0; i< 10000; i++) {
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            self.obj_atomic = [NSObject new];
        });
    }

atomic只是gettet、setter方法加锁,其他操作会有线程安全问题

@interface ViewController ()
@property (atomic , strong) NSString *info;
@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    //A
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            self.info = @"a";
            NSLog(@"A--info:%@", self.info);
        }
    });

    //B
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            self.info = @"b";
            NSLog(@"B--info:%@", self.info);
        }
    });    
    // 根据线程安全定义,如果atomic为线程安全A输出应该永远为A--info:a,B输出应该永远为B--info:b
    // NSlog会有:A--info:b
}
@end
@synchronized(obj)
  • synchronized是使用的递归mutex来做同步。
  • @synchronized(nil)不起任何作用

synchronized中传入的object的内存地址,被用作key,通过hash map对应的一个系统维护的递归锁。
所以不管是传入什么类型的object,只要是有内存地址,就能启动同步代码块的效果。

注意:需要合理使用obj,不是全部都用self

// 简单用法
@synchronized(obj) {
  //code
}

// 可以嵌套、递归
@synchronized(obj) {
      @synchronized(obj) {
      //code
    }
}
NSLocking

NSLocking协议定义了两个实例方法,lock和unlock对应着加锁与解锁

@protocol NSLocking

- (void)lock;
- (void)unlock;

@end

NSLock、NSConditionLock、NSRecursiveLock、NSCondition对应的实例都可以通过lock/unlock来进行加锁/解锁。

NSLock
@interface NSLock : NSObject <NSLocking> {
@private
    void *_priv;
}

// 尝试加锁,如果失败了,并不会阻塞线程,只是立即返回NO
- (BOOL)tryLock; 
// 是在指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO,阻塞线程。
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name;

@end

简单测试例子:

    //主线程中
    NSLock *lock = [[NSLock alloc] init];
    //线程1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"线程1");
        [lock unlock];
        NSLog(@"线程1解锁成功");
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [lock lock];
        NSLog(@"线程2");
        [lock unlock];
        NSLog(@"线程2解锁成功");
    });
NSConditionLock条件锁
@interface NSConditionLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (instancetype)initWithCondition:(NSInteger)condition NS_DESIGNATED_INITIALIZER;

@property (readonly) NSInteger condition;

- (void)lockWhenCondition:(NSInteger)condition;
- (void)unlockWithCondition:(NSInteger)condition;

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

- (BOOL)tryLockWhenCondition:(NSInteger)condition;
- (BOOL)lockWhenCondition:(NSInteger)condition beforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name;

@end

condition实现条件锁时(也可以不实现,直接调用协议方法lock),只有符合条件才能上锁,但是解锁为非条件,任意condition都可以解锁,此时设置的condition为下一次条件锁的condition。

伪代码

- (instancetype)initWithCondition:(NSInteger)condition {
        if (self =[ [NSConditionLock alloc] init]) {
               _condition = condition
        }
        return self;
}
// 利用condition加锁、解锁时伪代码是这样的
- (void)lockWhenCondition:(NSInteger)condition {
        if (_condition == condition) [self lock];
}
- (void)unlockWithCondition:(NSInteger)condition {
      [self setValue:@condition forKey:@"condition"];
      [self unlock];
}

简单例子:

    //主线程中
    NSConditionLock *theLock = [[NSConditionLock alloc] init];
    
    //线程1
    dispatch_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];
        }
    });
    
    //线程2
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        [theLock lockWhenCondition:2];
        NSLog(@"thread2");
        [theLock unlock];
    });
    
    /*运行结果
     2017-03-04 22:21:29.031 LockDemo[87455:3031878] thread1:0
     2017-03-04 22:21:31.105 LockDemo[87455:3031878] thread1:1
     2017-03-04 22:21:33.175 LockDemo[87455:3031878] thread1:2
     2017-03-04 22:21:35.249 LockDemo[87455:3031879] thread2
     */
NSRecursiveLock递归锁

NSRecursiveLock 是递归锁,他和 NSLock 的区别在于,NSRecursiveLock 可以在一个线程中重复加锁(反正单线程内任务是按顺序执行的,不会出现资源竞争问题),NSRecursiveLock 会记录上锁和解锁的次数,当二者平衡的时候,才会释放锁,其它线程才可以上锁成功。

@interface NSRecursiveLock : NSObject <NSLocking> {
@private
    void *_priv;
}

- (BOOL)tryLock;
- (BOOL)lockBeforeDate:(NSDate *)limit;

@property (nullable, copy) NSString *name ;

@end

简单例子

static int i = 10;

- (void)recursiveLock {
    [_lock lock];
    NSLog(@"NSRecursiveLock--%zd", i--);
    if (i >= 0) {
        [self recursiveLock];
    }
    [_lock unlock];
}
NSCondition

NSCondition中有这些方法

- (void)wait; //挂起线程
- (BOOL)waitUntilDate:(NSDate *)limit; //什么时候挂起线程
- (void)signal; // 唤醒一条挂起线程
- (void)broadcast; //唤醒所有挂起线程

使用例子:

    NSCondition *_lock = [NSCondition new];
    //A
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [_lock lock];
        NSLog(@"A线程加锁");
        [_lock wait];
        NSLog(@"A线程唤醒");
        [_lock unlock];
        NSLog(@"A线程解锁");
    });
    
    //B
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        [_lock lock];
        NSLog(@"B线程加锁");
        [_lock wait];
        NSLog(@"B线程唤醒");
        [_lock unlock];
        NSLog(@"B线程解锁");
    });
    
    
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(2);
        [_lock signal];// 唤醒一条线程
    });
    
    /*运行结果
     2017-10-20 18:00:59.848350+0800 debug-objc[41375:32423729] A线程加锁
     2017-10-20 18:00:59.848624+0800 debug-objc[41375:32423730] B线程加锁
     2017-10-20 18:01:01.853165+0800 debug-objc[41375:32423729] A线程唤醒
     2017-10-20 18:01:01.853236+0800 debug-objc[41375:32423729] A线程解锁
     */
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        sleep(2);
        [_lock broadcast];// 唤醒全部
    });
    
    /*运行结果
     2017-10-20 18:02:58.031822+0800 debug-objc[41428:32430448] A线程加锁
     2017-10-20 18:02:58.033322+0800 debug-objc[41428:32430447] B线程加锁
     2017-10-20 18:03:00.036548+0800 debug-objc[41428:32430448] A线程唤醒
     2017-10-20 18:03:00.037160+0800 debug-objc[41428:32430448] A线程解锁
     2017-10-20 18:03:00.037411+0800 debug-objc[41428:32430447] B线程唤醒
     2017-10-20 18:03:00.037532+0800 debug-objc[41428:32430447] B线程解锁
     */
GCD的dispatch_semaphore信号量
/*! 
 * @param value 信号量的起始值,当传入的值小于零时返回NULL
 * @result 成功返回一个新的信号量,失败返回NULL
 */
dispatch_semaphore_t dispatch_semaphore_create(long value)

/*!
 * @discussion 信号量减1,如果结果小于0,那么等待队列中信号增量到来直到timeout
 * @param dsema 信号量
 * @param timeout 等待时间,类型为dispatch_time_t,这里有两个宏DISPATCH_TIME_NOW、DISPATCH_TIME_FOREVER
 * @result 若等待成功返回0,timeout返回非0
 */
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);

/*!
 * @discussion 信号量加1,如果之前的信号量小于0,将唤醒一条等待线程
 * @param dsema  信号量
 * @result 唤醒一条线程返回非0,否则返回0
 */
long dispatch_semaphore_signal(dispatch_semaphore_t dsema)

简单例子

- (void)semaphore {
    dispatch_semaphore_t dsema = dispatch_semaphore_create(1);

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
            _info = @"a";
            NSLog(@"A--info:%@", _info);
            dispatch_semaphore_signal(dsema);
        }
    });

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
            _info = @"b";
            NSLog(@"B--info:%@", _info);
            dispatch_semaphore_signal(dsema);
        }
    });
}
GCD中“栅栏函数”:dispatch_barrier_async
    //同dispatch_queue_create函数生成的concurrent Dispatch Queue队列一起使用
    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"----1");
    });
    dispatch_async(queue, ^{
        NSLog(@"----2");
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"----3");
    });
    dispatch_async(queue, ^{
        NSLog(@"----4");
    });

上述代码打印结果总是1 2 –> barrier –>3 4,即1、2总在barrier之前打印,3、4总在barrier之后打印,其中1、2 由于并行处理先后顺序不定,当然3、4也一样。

OSSpinLock

OSSpinLock自旋锁,使用时需导入头文件#import <libkern/OSAtomic.h>
由于自旋锁存在优先级反转问题(可查看YYKit作者的这篇文章 不再安全的 OSSpinLock),在iOS 10.0中被<os/lock.h>中的os_unfair_lock()取代

    // 初始化 unlock为0,lock为非0
    OSSpinLock spinLock = OS_SPINLOCK_INIT;
    // 加锁
    OSSpinLockLock(&spinLock);
    // 解锁
    OSSpinLockUnlock(&spinLock);
    // 尝试加锁
    BOOL b = OSSpinLockTry(&spinLock);
- (void)OSSpinLock {

    OSSpinLock spinLock = OS_SPINLOCK_INIT;
    NSLog(@"加锁前:%zd", spinLock);
    OSSpinLockLock(&spinLock);
    NSLog(@"加锁后:%zd", spinLock);
    OSSpinLockUnlock(&spinLock);
    NSLog(@"解锁后:%zd", spinLock);
    /*运行结果
     2017-10-20 18:36:40.237586+0800 debug-objc[41870:32523780] 加锁前:0
     2017-10-20 18:36:40.237904+0800 debug-objc[41870:32523780] 加锁后:4294967295
     2017-10-20 18:36:40.237955+0800 debug-objc[41870:32523780] 解锁后:0
     */
}
os_unfair_lock

os_unfair_lock iOS 10.0新推出的锁,用于解决OSSpinLock优先级反转问题(用法与OSSpinLock差不多)

    // 初始化
    os_unfair_lock_t unfairLock = &(OS_UNFAIR_LOCK_INIT);
    // 加锁
    os_unfair_lock_lock(unfairLock);
    // 解锁
    os_unfair_lock_unlock(unfairLock);
    // 尝试加锁
    BOOL b = os_unfair_lock_trylock(unfairLock);

POSIX LOCK

POSIX LOCK为C语言级别的锁,需引入头像文件#import<pthread.h>

pthread_mutex_t

pthread_mutex_init(&lock, NULL);

PTHREAD_MUTEX_NORMAL 缺省类型,也就是普通锁。当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后先进先出原则获得锁。

PTHREAD_MUTEX_ERRORCHECK 检错锁,如果同一个线程请求同一个锁,则返回 EDEADLK,否则与普通锁类型动作相同。这样就保证当不允许多次加锁时不会出现嵌套情况下的死锁。

PTHREAD_MUTEX_RECURSIVE 递归锁,允许同一个线程对同一个锁成功获得多次,并通过多次 unlock 解锁。

PTHREAD_MUTEX_DEFAULT 适应锁,动作最简单的锁类型,仅等待解锁后重新竞争,没有等待队列。
static pthread_mutex_t lock;
- (void)pLock {

    pthread_mutex_init(&lock, NULL);

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            pthread_mutex_lock(&lock);
            _info = @"a";
            NSLog(@"A--info:%@", _info);
            pthread_mutex_unlock(&lock);
        }
    });

    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        while (1) {
            pthread_mutex_lock(&lock);
            _info = @"b";
            NSLog(@"B--info:%@", _info);
            pthread_mutex_unlock(&lock);
        }
    });
}

相关文章

  • OC--各种线程锁

    参考:正确使用多线程同步锁@synchronized()iOS中的锁iOS多线程安全详解iOS 常见知识点(三):...

  • Java多线程 - 各种线程锁

    多个线程同时对同一个对象进行读写操作,很容易会出现一些难以预料的问题。所以很多时候我们需要给代码块加锁,同一时刻只...

  • iOS线程同步(各种锁)

    线程安全 在iOS开发中经常会遇到一块资源被多个线程共享的情况,也就是多个线程会访问同一块资源,比如多个线程访问同...

  • 线程锁

    探讨iOS开发中各种锁使用NSCondition实现多线程同步 NSCondition是线程同步, 阻塞线程。 取...

  • Java 中的各种锁

    多线程开发离不开各种锁,下面总结下Java和JDK提供的各种锁机制 synchronized synchroniz...

  • 多线程(四)

    上篇多线程(三)我们看了多线程的安全隐患 以及各种锁的简单使用,接下来我们来看看锁的比较、以及自旋锁、互斥锁比较 ...

  • 多线程线程安全的各种锁

    文章转载自imlifengfeng的博客 一、概述 在多线程操作过程中,往往一个数据同时被多个线程读写,在这种情况...

  • 多线程:线程同步方案 各种锁

    iOS中的线程同步方案 OSSpinLock os_unfair_lock pthread_mutex dispa...

  • java并发

    1.并发编程中的锁 并发编程中的各种锁java高并发锁的3种实现Java并发机制及锁的实现原理 2.线程池核心线程...

  • Java-并发编程知识点总结

    目录: 线程基础 线程池 各种各样的锁 并发容器 原子类 Java 内存模型 线程协作 AQS 框架 一、线程基础...

网友评论

      本文标题:OC--各种线程锁

      本文链接:https://www.haomeiwen.com/subject/gnpvuxtx.html