JHHK

欢迎来到我的个人网站
行者常至 为者常成

参考:线程同步(二)

参考书籍 《多线程编程指南》
原著:Apple Inc.
翻译:謝業蘭 【老狼】

目录

使用锁

  1. 使用POSIX互斥锁
    为了锁住和解锁一个互斥锁,你可以使用 pthread_mutex_lock 和 pthread_mutex_unlock 函数。
    当你用完一个锁之后,只要简单的调用 pthread_mutex_destroy 来释放该锁的数据结构。

  2. 使用NSLock类

//应确保不同的线程使用的是同一把锁。否则保护会失效
self.moneyLock = [[NSLock alloc] init];

- (void)__saveMoney{

     //方式一:
    [self.moneyLock lock];
    //进行存钱业务...
    [self.moneyLock unlock];

    //方式二:
    //tryLock 试图获取一个锁,但是如果锁不可用的时候,它不会阻塞线程。相反,它只 是返回 NO。
    if([self.moneyLock tryLock]){
      //进行存钱业务...
      [self.moneyLock unlock];
    }


    //方式三:
    //lockBeforeDate:方法试图获取一个锁,但是如果锁没有在规定的时间内被获得,它会让线程从阻塞状态变为非阻塞状态(或者返回 NO)。
    if ([self.moneyLock lockBeforeDate:[NSDate dateWithTimeIntervalSinceNow:100000]]) {
        //进行存钱业务...
        [self.moneyLock unlock];
    }

}

- (void)__drawMoney{
    //方式一:
    [self.moneyLock lock];
    //进行取钱业务...
    [self.moneyLock unlock];

    //方式二:


    //方式三:
}

3.使用@synchronized指令

- (void)__saleTicket{
    static NSObject *lock;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        lock = [[NSObject alloc] init];
    });
    
    @synchronized(lock) {
        [super __saleTicket];
    }
}


- (void)__drawMoney{
    @synchronized([self class]) {
        [super __drawMoney];
    }
}

- (void)__saveMoney{
    @synchronized([self class]) { // objc_sync_enter
        [super __saveMoney];
    } // objc_sync_exit
}

4.使用 NSRecursiveLock 对象
NSRecursiveLock 类定义的锁可以在同一线程多次获得,而不会造成死锁。一个递归锁会跟踪它被多少次成功获得了。每次成功的获得该锁都必须平衡调用锁住和解 锁的操作。只有所有的锁住和解锁操作都平衡的时候,锁才真正被释放给其他线程获得。
递归锁:允许同一个线程对一把锁进行重复加锁

NSRecursiveLock *theLock = [[NSRecursiveLock alloc] init];
   

void MyRecursiveFunction(int value){
      
    [theLock lock];
      
    if (value != 0){ 
      --value; 
      MyRecursiveFunction(value);
    }

    [theLock unlock]; 
}

   
MyRecursiveFunction(5);

注意:因为一个递归锁不会被释放直到所有锁的调用平衡使用了解锁操作,所以你必须仔细权衡 是否决定使用锁对性能的潜在影响。长时间持有一个锁将会导致其他线程阻塞直到递归完成。如 果你可以重写你的代码来消除递归或消除使用一个递归锁,你可能会获得更好的性能。

5.使用 NSConditionLock 对象
NSConditionLock 对象定义了一个互斥锁,可以使用特定值来锁住和解锁。不要 把该类型的锁和条件(参见“条件”部分)混淆了。它的行为和条件有点类似,但是 它们的实现非常不同。

self.conditionLock = [[NSConditionLock alloc] initWithCondition:1];

[[[NSThread alloc] initWithTarget:self selector:@selector(__one) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__two) object:nil] start];   
[[[NSThread alloc] initWithTarget:self selector:@selector(__three) object:nil] start];

- (void)__one{
    [self.conditionLock lock];
    
    NSLog(@"__one");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:2];
}

- (void)__two{
    [self.conditionLock lockWhenCondition:2];
    
    NSLog(@"__two");
    sleep(1);
    
    [self.conditionLock unlockWithCondition:3];
}

- (void)__three{
    [self.conditionLock lockWhenCondition:3];
    
    NSLog(@"__three");
    
    [self.conditionLock unlock];
}

6.读写锁
在读写过程中如何既保证读写效率又保证数据安全;

多读单写
同一时间,只能有1个线程,进行写操作。   
同一时间,允许有多个线程,进行读操作。   
同一时间,不允许既有写操作又有读操作。

img

iOS实现多读单写的方式:

方式一:pthread_rwlock 读写锁

@property (assign, nonatomic) pthread_rwlock_t lock;

// 初始化锁
pthread_rwlock_init(&_lock, NULL);

//异步调用
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++) {
    dispatch_async(queue, ^{
        [self read];
    });
    dispatch_async(queue, ^{
        [self write];
    });
}


- (void)read {
    pthread_rwlock_rdlock(&_lock);
    
    sleep(1);
    NSLog(@"%s", __func__);
    
    pthread_rwlock_unlock(&_lock);
}

- (void)write{
    pthread_rwlock_wrlock(&_lock);
    
    sleep(1);
    NSLog(@"%s", __func__);
    
    pthread_rwlock_unlock(&_lock);
}

- (void)dealloc{
    //销毁锁
    pthread_rwlock_destroy(&_lock);
}

方式二:dispatch_barrier_async 异步栅栏调用

//必须手动创建并发队列
self.queue = dispatch_queue_create("rw_queue", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0; i < 10; i++) {
    dispatch_async(self.queue, ^{
        [self read];
    });
    
    dispatch_async(self.queue, ^{
        [self read];
    });
    
    dispatch_async(self.queue, ^{
        [self read];
    });
    
    dispatch_barrier_async(self.queue, ^{
        [self write];
    });
}

- (void)read {
    sleep(1);
    NSLog(@"read");
}

- (void)write{
    sleep(1);
    NSLog(@"write");
}

使用条件

条件是一个特殊类型的锁,你可以使用它来同步操作必须处理的顺序。它们和互斥锁有微妙的不同。一个线程等待条件会一直处于阻塞状态直到条件获得其他线程显式发出的信号。

  1. 使用NSCondition类
self.condition = [[NSCondition alloc] init];
self.data = [NSMutableArray array];

[[[NSThread alloc] initWithTarget:self selector:@selector(__remove) object:nil] start];
[[[NSThread alloc] initWithTarget:self selector:@selector(__add) object:nil] start];

// 生产者-消费者模式

// 线程1
// 删除数组中的元素
- (void)__remove{
    [self.condition lock];
    NSLog(@"__remove - begin");
    
    if (self.data.count == 0) {
        // 等待
        [self.condition wait];
    }
    
    [self.data removeLastObject];
    NSLog(@"删除了元素");
    
    [self.condition unlock];
}

// 线程2
// 往数组中添加元素
- (void)__add{
    [self.condition lock];
    
    sleep(1);
    
    [self.data addObject:@"Test"];
    NSLog(@"添加了元素");
    
    //激活一个等待该条件的线程
    [self.condition signal];
    
    //激活所有等待该条件的线程
    //[self.condition broadcast];
    
    sleep(2);
    
    [self.condition unlock];
}

2.semaphore

//信号量的初始化
long value = 1;//1相当于串行,相当于最大并发数
self.semaphore = dispatch_semaphore_create(value);


//如果信号量<=0,当前线程就会进入休眠等待,知道信号量>0
//如果信号量>0,就减去1,当前线程向下执行代码
dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);

//Do something ....

//让信号量的值加1
dispatch_semaphore_signal(self.semaphore);

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.