多线程的那些锁

作者: coderhlt | 来源:发表于2019-05-08 14:07 被阅读7次

    一、OSSpinLock

    自旋锁: 等待锁的线程会处于忙等(busy-wait)状态,一直占着CPU的资源。(假如有A、B、C三个线程,A第一个获得锁,B、C三个线程将处循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环)

    导入头文件#import <libkern/OSAtomic.h>
    // 初始化锁
    OSSpinLock lock = OS_SPINLOCK_INIT;
    
    // 加锁
    OSSpinLockLock(&_lock);
    
    加锁 和 解锁 中间放 多个线程访问的资源
    
    // 解锁
    OSSpinLockUnlock(&_lock);
    

    二、os_unfair_lock

    互斥锁:等待的线程会处于休眠状态。

    • 从底层调用看(下面会从汇编角度来分析自旋锁和互斥锁),等待os-unfair-lock锁的线程会处于休眠状态,而并非忙等,所以说虽然os-unfair-lock用于取代OSSpinLock,但是os-unfair-lock并不是自旋锁。
    #import "ViewController.h"
    #import <os/lock.h>
    @interface ViewController (){
        os_unfair_lock lock;
    }
    @property (assign, nonatomic) int ticketsCount;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
     
        lock = OS_UNFAIR_LOCK_INIT;
        
        
        [self ticketTest];
        
        
    }
    
    - (void)saleTicket
    {
        os_unfair_lock_lock(&lock);
        
        int oldTicketsCount = self.ticketsCount;
        sleep(.2);
        oldTicketsCount--;
        self.ticketsCount = oldTicketsCount;
        NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
        
        // 解锁
        os_unfair_lock_unlock(&lock);
    }
    - (void)ticketTest
    {
        self.ticketsCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
    }
    
    @end
    
    

    三、pthread_mutex

    • 互斥锁


      屏幕快照 2019-05-08 下午1.58.30.png
    #import "ViewController.h"
    #import <pthread.h>
    @interface ViewController ()
    @property (assign, nonatomic) int ticketsCount;
    @property(nonatomic,assign)pthread_mutex_t mutex;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
       // 初始化锁的属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_NORMAL);
        
        //初始化锁
        pthread_mutex_init(&_mutex, &attr);
        
        //销毁锁的属性
        pthread_mutexattr_destroy(&attr);
        
        [self ticketTest];
        
        
    }
    
    - (void)saleTicket
    {
        //加锁
        pthread_mutex_lock(&_mutex);
        int oldTicketsCount = self.ticketsCount;
        sleep(.2);
        oldTicketsCount--;
        self.ticketsCount = oldTicketsCount;
        NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
        
        //解锁
        pthread_mutex_unlock(&_mutex);
    }
    
    //销毁锁
    - (void)dealloc{
    
        pthread_mutex_destroy(&_mutex);
        
    }
    
    - (void)ticketTest
    {
        self.ticketsCount = 15;
        
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
        
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
    }
    @end
    

    3.1、递归锁

    #import "ViewController.h"
    #import <pthread.h>
    @interface ViewController ()
    @property(nonatomic,assign)pthread_mutex_t mutex;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
    
        //初始化锁
        pthread_mutex_init(&_mutex, NULL);
    
        [self test1];
       
    }
    
    - (void)test1{
        //加锁
        pthread_mutex_lock(&_mutex);
        
        NSLog(@"测试一");
        static int i = 0;
        I++;
        if (i<10) {
         [self test1];
        }
        //解锁
        pthread_mutex_unlock(&_mutex);
    }
    
    • 初始化属性传Null,可以正常使用
    • 以上递归调用会产生死锁现象

    解决方案:设置为递归锁
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);

    #import "ViewController.h"
    #import <pthread.h>
    @interface ViewController ()
    @property (assign, nonatomic) int ticketsCount;
    @property(nonatomic,assign)pthread_mutex_t mutex;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
       // 初始化锁的属性
        pthread_mutexattr_t attr;
        pthread_mutexattr_init(&attr);
        pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
        
        //初始化锁
        pthread_mutex_init(&_mutex, &attr);
        
        //销毁锁的属性
        pthread_mutexattr_destroy(&attr);
     
        [self test1];
      
    }
    
    - (void)test1{
        //加锁
        pthread_mutex_lock(&_mutex);
        
        NSLog(@"测试一");
        static int i = 0;
        I++;
        if (i<10) {
         [self test1];
        }
        //解锁
        pthread_mutex_unlock(&_mutex);
        
    }
    
    
    • 递归锁允许同一线程重复加锁、解锁。但不允许不同线程重复加锁,这样既保证了线程安全又解决了线程中递归调用。

    3.1、条件

    屏幕快照 2019-05-08 下午4.12.04.png

    需求描述:开启两个线程,线程一给可变的array删除最后一个元素,线程二给可变的array添加元素。如果array.count = 0则不需要删除元素。

    #import "ViewController.h"
    #import <pthread.h>
    @interface ViewController ()
    @property(nonatomic,strong)NSMutableArray *array;
    @property(nonatomic,assign)pthread_mutex_t mutex;
    @property(nonatomic,assign)pthread_cond_t condtion;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
        self.array = [NSMutableArray array];
        
        //初始化条件
        pthread_cond_init(&_condtion, NULL);
        
        //初始化锁
        pthread_mutex_init(&_mutex, NULL);
        
        [[[NSThread alloc]initWithTarget:self selector:@selector(removeData) object:nil]start];
        
        sleep(2);
        
        [[[NSThread alloc]initWithTarget:self selector:@selector(addData) object:nil]start];
    
    }
    
    - (void)removeData{
        //加锁
        pthread_mutex_lock(&_mutex);
        NSLog(@"remove");
        if (self.array.count == 0) {
            pthread_cond_wait(&_condtion, &_mutex);
        }
        [self.array removeLastObject];
        NSLog(@"删除元素");
        pthread_mutex_unlock(&_mutex);
        
    }
    
    - (void)addData{
    
        //加锁
        pthread_mutex_lock(&_mutex);
    
        [self.array addObject:@"0"];
        
        NSLog(@"添加元素");
    
        pthread_cond_signal(&_condtion);
        
        //解锁
        pthread_mutex_unlock(&_mutex);
    }
    
    
    屏幕快照 2019-05-08 下午4.23.00.png

    当 线程一执行了第43行: pthread_cond_wait(&_condtion, &_mutex);这句代码,线程一会放开锁进入休眠等待状态,线程二就可以获得锁进行加锁。当线程二执行了 pthread_cond_signal(&_condtion);这句代码后,线程一会被唤醒再次加上锁,开始从第45行代码之后继续执行。因此不管怎么样打印的结果都是先添加元素,再删除元素。总结来说pthread_mutex的应用场景应该是线程依赖。

    四、OC版的锁

    • NSLock:是对mutex普通锁的封装
    • NSRecursiveLock:是对mutex递归锁的封装
    • NSCondition:是对mutex和cond的封装
    • NSConditionLock是对NSCondition的进一步封装

    4.1、 NSLock

    屏幕快照 2019-05-09 上午9.48.45.png
    #import <Foundation/NSObject.h>
    #import "ViewController.h"
    @interface ViewController ()
    @property(nonatomic,assign)int ticketsCount;
    @property(nonatomic,strong)NSLock *lock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
    
        self.lock = [[NSLock alloc]init];
        
        [self ticketTest];
        
    }
    - (void)saleTicket
    {
      //加锁
        [self.lock lock];
        
        int oldTicketsCount = self.ticketsCount;
        sleep(.2);
        oldTicketsCount--;
        self.ticketsCount = oldTicketsCount;
        NSLog(@"还剩%d张票 - %@", oldTicketsCount, [NSThread currentThread]);
    
         //解锁
        [self.lock unlock];
    }
    
    
    - (void)ticketTest
    {
        self.ticketsCount = 15;
    
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
    
        dispatch_async(queue, ^{
            for (int i = 0; i < 5; i++) {
                [self saleTicket];
            }
        });
    }
    
    

    4.2、NSRecursiveLock

    #import "ViewController.h"
    @interface ViewController ()
    @property(nonatomic,strong)NSRecursiveLock *recursiveLock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
    
        self.recursiveLock = [[NSRecursiveLock alloc]init];
        
        [self test1];
        
    }
    - (void)test1{
        //加锁
        [self.recursiveLock lock];
        
        NSLog(@"测试一");
        static int i = 0;
        I++;
        if (i<10) {
            [self test1];
        }
        //解锁
        [self.recursiveLock unlock];
    }
    

    4.3、NSCondition

    屏幕快照 2019-05-09 上午9.49.07.png
    #import "ViewController.h"
    @interface ViewController ()
    @property(nonatomic,strong)NSMutableArray *array;
    @property(nonatomic,assign)int ticketsCount;
    @property(nonatomic,strong)NSCondition *condition;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        
    
        self.array = [NSMutableArray array];
    
        self.condition = [[NSCondition alloc]init];
        
        
        [[[NSThread alloc]initWithTarget:self selector:@selector(removeData) object:nil]start];
        
        sleep(2);
        
        [[[NSThread alloc]initWithTarget:self selector:@selector(addData) object:nil]start];
    
        
    }
    
    - (void)removeData{
        [self.condition lock];
        NSLog(@"remove");
        if (self.array.count == 0) {
            [self.lockcondition wait];
        }
        [self.array removeLastObject];
        NSLog(@"删除元素");
       [self.condition unlock];
        
    }
    
    - (void)addData{
        
        [self.condition lock];
        [self.array addObject:@"0"];
        
        NSLog(@"添加元素");
       
        [self.condition signal];
    
        [self.condition unlock];
    }
    

    4.4、NSConditionLock

    屏幕快照 2019-05-09 上午9.51.21.png
    #import "ViewController.h"
    @interface ViewController ()
    @property(nonatomic,strong)NSConditionLock *conditionlock;
    @end
    
    @implementation ViewController
    
    - (void)viewDidLoad {
        [super viewDidLoad];
        self.conditionlock = [[NSConditionLock alloc]initWithCondition:1];
    
        [self test];
        
    }
    - (void)test{
     
        dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
        
        dispatch_async(queue, ^{
            [self.conditionlock lockWhenCondition:2];
            
            NSLog(@"线程2");
            
            [self.conditionlock unlock];
            
        });
        
        sleep(10);
        dispatch_async(queue, ^{
            [self.conditionlock lockWhenCondition:1];
            
            NSLog(@"线程1");
            
            [self.conditionlock unlockWithCondition:2];
        });
        
    }
    
    屏幕快照 2019-05-09 下午3.52.53.png

    相关文章

      网友评论

        本文标题:多线程的那些锁

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