作者: 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