作者: iPhone | 来源:发表于2020-08-15 10:49 被阅读0次

1.###锁的类型

1.互斥锁:保证同时只有一个线程能够访问。当获取锁操作失败时,线程进入休眠,等待锁被释放时被唤醒。
2.自旋锁:保证同时只有一个线程能够访问(这点和互斥锁相同);只是自旋锁不会引起调用者的休眠。
如果自旋锁已经被别的执行单元持有,调用者就一直循环尝试,直到该自旋锁被持有者释放。
因为没有休眠,所以效率高于互斥锁;但是,性能方面一般。
3.递归锁: 加了递归功能的互斥锁。

2.###优缺点

1.自旋锁的循环访问,会占用CPU资源,降低CPU的效率;
2.在使用自旋锁时有可能造成死锁,当地柜调用时有可能造成死锁;

3.加锁原理

自旋锁:线程一直runing(加锁->解锁),检查锁状态,机制较简单。
互斥锁:线程sleep(加锁)-> running (解锁),过程中有上下文的切换,CPU的抢占,信号的发送等开销。

4.###常用的锁

1.@synchronized

@synchronized(self) {
}

结论:1.是对互斥锁的一种封装;2.具体点,是种递归锁,内部搭配nil防止死锁;3.通过表的结构存要锁的对象;4.表内部的对象又是通过哈希存储的。
坑点:在大量线程异步同时操作同一个对象时,因为递归锁会不停的alloc/release,在某一个对象会是nil;而此时@synchronized(obj) 会判断obj == nil,就不会再加锁,导致线程访问冲突。

2.NSLock

NSlock 可以解决大量异步线程同时操作同一个对象的内存安全问题;
NSLock 是对pthread_mutex(互斥锁)的封装,有超时控制。
坑点:当NSLock对同一个线程加锁两次,就会造成死锁;即不能实现递归锁。

//NSLock
- (void)NSLock_crash {
    NSLock *lock = [[NSLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        static void (^testBlock)(int);
        testBlock = ^(int value) {
            [lock lock];
            if (value > 0) {
                NSLog(@"value-->%d",value);
                testBlock(value-1);//递归调用,用递归锁
            }
            [lock unlock];
        };
        testBlock(10);
    });
}

//递归锁NSRecursiveLock
- (void)NSRecursiveLock_NO_crash {
    NSRecursiveLock *lock = [[NSRecursiveLock alloc] init];
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        static void (^testBlock)(int);
        testBlock = ^(int value) {
            [lock lock];
            if (value > 0) {
                NSLog(@"value-->%d",value);
                testBlock(value-1);//递归调用,用递归锁 , 同一线程多次加锁
            }
            [lock unlock];
        };
        testBlock(10);
    });
}

3. NSRecursiveLock

1.是对pthread_mutex(互斥锁)的封装,不同的是加Recursive递归调用功能;
2.同样也有timeout超时控制;

4. NSCondition 使用的相对较少

1.也是对互斥锁的封装;2.使用了wait信号量可以让当前线程处于等到中;3.使用signal信号可以告诉其他某一个线程不用再等待了,可以继续执行;4.内部还有一个broadcast(广播)信号,用于发送(signal)信号给其他所有线程用法;

5.NSConditionLock类似于信号量

1.NSConditionLock 是 对NSCondition+线程数的封装,即NSConditionLock = NSCondition + lock
internal var _thread: _swift_CFThreadRef?:_thread就是当前可以同事操作的线程数,通过搭配NSCondition可以达到dispatch_semaphore的效果
lock(before: Date.distantFuture):也有超时时间

- (void)testConditonLock{
    // 信号量
    NSConditionLock *conditionLock = [[NSConditionLock alloc] initWithCondition:2];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
       [conditionLock lockWhenCondition:1];
       NSLog(@"线程 1");
       [conditionLock unlockWithCondition:0];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
       
       [conditionLock lockWhenCondition:2];
       
       NSLog(@"线程 2");
       
       [conditionLock unlockWithCondition:1];
    });
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
       
       [conditionLock lock];
       NSLog(@"线程 3");
       [conditionLock unlock];
    });
}

6.dispatch_semaphore

1.dispathch_semaphore 是GCD用来同步的一种方式,与他相关的只有三个函数,一个是创建信号量,一个是等待信号量,一个是发送信号量。
dispathch_semaphore NSConditionLock NSCOnditin ,都是基于信号量同步方式,但是NSCondition信号智能发送,不能保存(如果没有线程在等待,则发送信号量会失败);而dispathch_semaphore 能保存发送的信号。

2.dispatch_semaphore 的核心是 dispatch_semaphore_t 类型的信号量。
dispatch_semaphore_create(1) 方法可以创建一个 dispatch_semaphore_t 类型的信号量,设定信号量的初始值为 1。注意,这里的传入的参数必须大于或等于 0,否则 dispatch_semaphore_create 会返回 NULL。
dispatch_semaphore_wait(signal, overTime); 方法会判断 signal 的信号值是否大于 0。大于 0 不会阻塞线程,消耗掉一个信号,执行后续任务。如果信号值为 0,该线程会和 NSCondition 一样直接进入 waiting 状态,等待其他线程发送信号唤醒线程去执行后续任务,或者当 overTime 时限到了,也会执行后续任务。
dispatch_semaphore_signal(signal); 发送信号,如果没有等待的线程接受信号,则使 signal 信号值加一(做到对信号的保存)。

3.一个 dispatch_semaphore_wait(signal, overTime); 方法会去对应一个 dispatch_semaphore_signal(signal); 看起来像 NSLock 的 lock 和 unlock,其实可以这样理解,区别只在于有信号量这个参数,lock unlock 只能同一时间,一个线程访问被保护的临界区,而如果 dispatch_semaphore 的信号量初始值为 x ,则可以有 x 个线程同时访问被保护的临界区。

7.OSSpinLock - os_unfair_lock

在iOS10 之前,OSSpinLock 是一种自旋锁,也只有加锁,解锁,尝试加锁三个方法。和 NSLock 不同的是 NSLock 请求加锁失败的话,会先轮询,但一秒过后便会使线程进入 waiting 状态,等待唤醒。而 OSSpinLock 会一直轮询,等待时会消耗大量 CPU 资源,不适用于较长时间的任务。而因为OSSpinLock不再线程安全,在iOS10之后OSSpinLock被废弃内部封装了os_unfair_lockos_unfair_lock也是一种互斥锁不会忙等。 `typedef int32_t OSSpinLock OSSPINLOCK_DEPRECATED_REPLACE_WITH(os_unfair_lock);

8.读写锁

读写锁是一种特殊的自旋锁,他能做到多读单写;实现: 并发队列+ dispatch_barrier_async

########### .h文件
#import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface RF_RWLock : NSObject
// 读数据
- (id)rf_objectForKey:(NSString *)key;
// 写数据
- (void)rf_setObject:(id)obj forKey:(NSString *)key;
@end

NS_ASSUME_NONNULL_END

########### .m文件
#import "RF_RWLock.h"

@interface RF_RWLock ()
// 定义一个并发队列:
@property (nonatomic, strong) dispatch_queue_t concurrent_queue;
// 用户数据中心, 可能多个线程需要数据访问:
@property (nonatomic, strong) NSMutableDictionary *dataCenterDic;
@end

@implementation RF_RWLock

- (id)init{
    self = [super init];
    if (self){
        // 创建一个并发队列:
        self.concurrent_queue = dispatch_queue_create("read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        // 创建数据字典:
        self.dataCenterDic = [NSMutableDictionary dictionary];
    }
    return self;
}

#pragma mark - 读数据
- (id)rf_objectForKey:(NSString *)key{
    __block id obj;
    // 同步读取指定数据:
    dispatch_sync(self.concurrent_queue, ^{
        obj = [self.dataCenterDic objectForKey:key];
    });
    return obj;
}

#pragma mark - 写数据
- (void)rf_setObject:(id)obj forKey:(NSString *)key{
    // 异步栅栏调用设置数据:
    dispatch_barrier_async(self.concurrent_queue, ^{
        [self.dataCenterDic setObject:obj forKey:key];
    });
}

@end

相关文章

网友评论

      本文标题:

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