iOS一些实用的东西iOS MultiThreadiOS 开发

8. iOS中的多线程——锁

2016-04-02  本文已影响866人  LynnXYT

题记:虽然有些事情的发生可能是你预料之中的,但是当它真正的发生了的时候,还是很难以接受的,还是需要一点时间,去缓和这种消极的情绪,尽快站起来吧!加油!花了一天半的时间,各种查阅资料,总结了iOS中关于线程锁的知识,希望我能从中学到一些,也希望可以帮到同样有需要的你!(文中如有错误,还请提出,一起交流)


本文主要介绍:

1. 互斥锁(Mutex)

常用,当一个线程试图获取被另一个线程占用的锁时,它就会被挂起,让出CPU,直到该锁被释放。

1.@synchronized 同步锁

/**
 *设置属性值
 */
-(void)setMyTestString:(NSString *)myTestString{
    @synchronized(self) {
        // todo something
        _myTestString = myTestString;
    }
}
+(instancetype)shareInstance{
    // 1.定义一个静态实例,初值nil
    static TestSynchronized *myClass = nil;
    // 2.添加同步锁,创建实例
    @synchronized(self) {
        // 3.判断实例是否创建过,创建过则退出同步锁,直接返回该实例
        if (!myClass) {
            // 4.未创建过,则新建一个实例并返回
            myClass = [[self alloc] init];
        }
    }
    return myClass;
}
此时为了保证单例模式的更加严谨,需要重写`allocWithZone`方法,保证其他开发者使用`alloc`和`init`方法时,不再创建新的对象。必要的时候还需要重写`copyWithZone`方法防止`copy`属性对单例模式的影响。

iOS中还有一种更加轻便的方法实现单例模式,即使用GCD中的dispatch_once函数实现。

例程:

+(instancetype)shareInstance{
    static TestSynchronized *myClass = nil;
    static dispatch_once_t once_token;
    dispatch_once(&once_token, ^{
        myClass = [[self alloc] init];
    });
    return myClass;
}

2.NSLock

static NSLock *mylock;
-(void)viewDidLoad {
    [super viewDidLoad];
    mylock = [[NSLock alloc] init];
}
-(void)myLockTest1{
    if ([mylock tryLock]) {
        // to do something
        [mylock unlock];
    }
}
-(void)myLockTest2{
    [mylock lock];
    // to do something
    [mylock unlock];
}
2. 递归锁(Recursive Lock)
NSRecursiveLock *myRecursiveLock = [[NSRecursiveLock alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        static void (^MyRecursiveLockBlk)(int value);
        MyRecursiveLockBlk = ^(int value){
            [myRecursiveLock lock];
            if (value > 0) {
                // to do something
                NSLog(@"MyRecursiveLockBlk value = %d", value);
                MyRecursiveLockBlk(value - 1);
            }
            [myRecursiveLock unlock];
        };
        MyRecursiveLockBlk(6);
    });

此时如果将例程中的递归锁换成互斥锁:
NSRecursiveLock *myRecursiveLock = [[NSRecursiveLock alloc] init];换成
NSLock *myLock = [[NSLock alloc] init];,则会发生死锁问题。

3. 读写锁(Read-write Lock)
#import "ViewController.h"
#import <pthread.h>
@interface ViewController ()
@property(nonatomic, copy) NSString *rwStr;
@end
@implementation ViewController
pthread_rwlock_t rwlock;
-(void)viewDidLoad {
    [super viewDidLoad];
    // 初始化读写锁
    pthread_rwlock_init(&rwlock,NULL);
    __block int i;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        i = 5;
        while (i>=0) {
            NSString *temp = [NSString stringWithFormat:@"writing == %d", i];
            [self writingLock:temp];
            i--;
        }  
    });
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        i = 5;
        while (i>=0) {
            [self readingLock];
            i--;
        }
    });
}
// 写加锁
-(void)writingLock:(NSString *)temp{
    pthread_rwlock_wrlock(&rwlock);
    // writing
    self.rwStr = temp;
    NSLog(@"%@", temp);
    pthread_rwlock_unlock(&rwlock);
}
// 读加锁
-(NSString *)readingLock{
    pthread_rwlock_rdlock(&rwlock);
    // reading
    NSString *str = self.rwStr;
    NSLog(@"reading == %@",self.rwStr);
    pthread_rwlock_unlock(&rwlock);
    return str;
}
@end
4. 自旋锁(Spin Lock)
// 头文件
#import <libkern/OSAtomic.h>
// 初始化自旋锁
static OSSpinLock myLock = OS_SPINLOCK_INIT;
// 自旋锁的使用
-(void)SpinLockTest{
    OSSpinLockLock(&myLock);
    // to do something
    OSSpinLockUnlock(&myLock);
}
5. 分布锁(Didtributed Lock)
6. 条件变量(Condition Variable)
    // 创建锁
     NSCondition *condition = [[NSCondition alloc] init];
    static int count = 0;
    // 生产者
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while(count<20)
        {
            [condition lock];
            // 生产
            count ++;
            NSLog(@"生产 = %d",count);
            [condition signal];
            [condition unlock];
        }
    });
    // 消费者
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (count>0)
        {
            [condition lock];
            // 消耗
            count --;
            NSLog(@"消耗剩余 = %d",count);
            [condition unlock];
        }
    });

NSConditionLock的例程:

    // 创建锁
    NSConditionLock *condLock = [[NSConditionLock alloc] initWithCondition:ConditionHASNOT];
    static int count = 0;
    // 生产者
    while(true)
    {
        [condLock lock];
        // 生产
        count ++;
        [condLock unlockWithCondition:ConditionHAS];
    }
    // 消费者
    while (true)
    {
        [condLock lockWhenCondition:ConditionHAS];
        // 消耗
        count --;
        [condLock unlockWithCondition:(count<=0 ? ConditionHASNOT : ConditionHAS)];
    }
7. 信号量(Semaphore)
8. 栅栏/屏障(Barrier)

iOS中线程锁的性能对比:

点击这里,参考网址


> 划重点:自旋锁是线程不安全的在 ibireme 的 不再安全的 OSSpinLock有解释,进一步的ibireme在文中也有提到苹果在新系统中已经优化了 pthread_mutex 的性能,所以它看上去和 OSSpinLock 差距并没有那么大,所以笔者觉得不妨多了解了解pthread_mutex
    __block pthread_mutex_t theLock;
    pthread_mutex_init(&theLock, NULL);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            pthread_mutex_lock(&theLock);
            NSLog(@"需要线程同步的操作1 开始");
            sleep(3);
            NSLog(@"需要线程同步的操作1 结束");
            pthread_mutex_unlock(&theLock);
        
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            sleep(1);
            pthread_mutex_lock(&theLock);
            NSLog(@"需要线程同步的操作2");
            pthread_mutex_unlock(&theLock);
        
    });

c语言定义下多线程加锁方式。

  1. pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t attr);
    初始化锁变量mutex。attr为锁属性,NULL值为默认属性。
  2. pthread_mutex_lock(pthread_mutex_t* mutex);加锁
  3. pthread_mutex_tylock(pthread_mutex_t* mutex);加锁,但是与2不一样的是当锁已经在使用的时候,返回为EBUSY,而不是挂起等待。
  4. pthread_mutex_unlock(pthread_mutex_t* mutex);释放锁
  5. pthread_mutex_destroy(pthread_mutex_t* *mutex);使用完后释放

代码执行操作结果如下:

2016-06-30 21:13:32.440 SafeMultiThread[31429:548869] 需要线程同步的操作1 开始
2016-06-30 21:13:35.445 SafeMultiThread[31429:548869] 需要线程同步的操作1 结束
2016-06-30 21:13:35.446 SafeMultiThread[31429:548866] 需要线程同步的操作2

    __block pthread_mutex_t theLock;
    //pthread_mutex_init(&theLock, NULL);
    
    pthread_mutexattr_t attr;
    pthread_mutexattr_init(&attr);
    pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
    pthread_mutex_init(&lock, &attr);
    pthread_mutexattr_destroy(&attr);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        
        static void (^RecursiveMethod)(int);
        
        RecursiveMethod = ^(int value) {
            
            pthread_mutex_lock(&theLock);
            if (value > 0) {
                
                NSLog(@"value = %d", value);
                sleep(1);
                RecursiveMethod(value - 1);
            }
            pthread_mutex_unlock(&theLock);
        };
        
        RecursiveMethod(5);
    });

这是pthread_mutex为了防止在递归的情况下出现死锁而出现的递归锁。作用和NSRecursiveLock递归锁类似。
如果使用pthread_mutex_init(&theLock, NULL);初始化锁的话,上面的代码会出现死锁现象,但是改成使用递归锁的形式,则没有问题。

上一篇下一篇

猜你喜欢

热点阅读