iOS Sharing #03 | 2019-04-06

目录

一、atomic关键字内部使用的是什么锁?

二、串行同步、串行异步、并发同步、并发异步各自会开几条线程?

三、为何须要在主线程更新UI?

四、iOS中如何用多线程实现多读单写?

五、iOS多线程中有多少种方式能够作到等待前面线程执行完毕再执行后面的线程?


一、atomic关键字内部使用的是什么锁?

答:

首先了解一些基本概念:git

  • 临界区:指的是一块对公共资源进行访问的代码,并不是一种机制或是算法。github

  • 自旋锁:是用于多线程同步的一种锁,线程反复检查锁变量是否可用。因为线程在这一过程当中保持执行,所以是一种忙等待。一旦获取了自旋锁,线程会一直保持该锁,直至显式释放自旋锁。 自旋锁避免了进程上下文的调度开销,所以对于线程只会阻塞很短期的场合是有效的。算法

  • 互斥锁(Mutex):是一种用于多线程编程中,防止两条线程同时对同一公共资源(好比全局变量)进行读写的机制。该目的经过将代码切片成一个一个的临界区而达成。编程

  • 读写锁:是计算机程序的并发控制的一种同步机制,也称“共享-互斥锁”、多读者-单写者锁) 用于解决多线程对公共资源读写问题。读操做可并发重入,写操做是互斥的。 读写锁一般用互斥锁、条件变量、信号量实现。安全

  • 信号量(semaphore):是一种更高级的同步机制,互斥锁能够说是semaphore在仅取值0/1时的特例。信号量能够有更多的取值空间,用来实现更加复杂的同步,而不仅仅是线程间互斥。bash

  • 条件锁:就是条件变量,当进程的某些资源要求不知足时就进入休眠,也就是锁住了。当资源被分配到了,条件锁打开,进程继续运行。多线程

  • 死锁:指两个或两个以上的进程在执行过程当中,因为竞争资源或者因为彼此通讯而形成的一种阻塞的现象,若无外力做用,它们都将没法推动下去,这些永远在互相等待的进程称为死锁进程。并发

  • 轮询(Polling):一种CPU决策如何提供周边设备服务的方式,又称“程控输出入”。轮询法的概念是,由CPU定时发出询问,依序询问每个周边设备是否须要其服务,有即给予服务,服务结束后再问下一个周边,接着不断周而复始。app

锁的类型:异步

  • 互斥锁

    • NSLock
    • pthread_mutex
    • pthread_mutex(recursive)递归锁
    • @synchronized
  • 自旋锁

    • OSSpinLock
    • os_unfair_lock
  • 读写锁

    • pthread_rwlock
  • 递归锁

    • NSRecursiveLock

    • pthread_mutex(recursive)(见上)

  • 条件锁

    • NSCondition
    • NSConditionLock
  • 信号量

    • dispatch_semaphore

time

//10000000
OSSpinLock:                 112.38 ms
dispatch_semaphore:         160.37 ms
os_unfair_lock:             208.87 ms
pthread_mutex:              302.07 ms
NSCondition:                320.11 ms
NSLock:                     331.80 ms
pthread_rwlock:             360.81 ms
pthread_mutex(recursive):   512.17 ms
NSRecursiveLock:            667.55 ms
NSConditionLock:            999.91 ms
@synchronized:             1654.92 ms

//1000
OSSpinLock:                   0.02 ms
dispatch_semaphore:           0.03 ms
os_unfair_lock:               0.04 ms
pthread_mutex:                0.06 ms
NSLock:                       0.06 ms
pthread_rwlock:               0.07 ms
NSCondition:                  0.07 ms
pthread_mutex(recursive):     0.09 ms
NSRecursiveLock:              0.12 ms
NSConditionLock:              0.18 ms
@synchronized:                0.33 ms
复制代码

atomic

atomic使用的是自旋锁,主要用于赋值操做等轻量操做(散列表,引用计数,弱引用指针赋值),而互斥锁通常都是锁线程,好比单例。


二、串行同步、串行异步、并发同步、并发异步各自会开几条线程?

答:

首先了解一些基本概念:

  • 同步:只能在当前线程中执行任务,不具有开启新线程的能力
  • 异步:能够在新的线程中执行任务,具有开启新线程的能力
  • 串行:一个任务执行完毕后,再执行下一个任务
  • 并发:多个任务并发(同时)执行

因此:

  • 串行同步不开新线程
  • 串行异步开启一条新线程
  • 并发同步不开新线程
  • 并发异步开启多条

三、为何须要在主线程更新UI?

答:
  • 安全

在非主线程中更新UI就会有多个线程同时操做一个控件的可能,形成最后更新的结果不符合预期

  • 效率

多线程自己就是为了并发处理以达到高效的目的,可是刷新UI使用并发会形成安全问题,要解决上面的安全问题,那就须要给控件加锁,可是加锁必然会形成额外的开销,同时开新的线程自己就有必定的开销,因此不如直接在主线程中执行更新操做。


四、iOS中如何用多线程实现多读单写?

答:
@interface CustomDictionary ()

//多线程须要访问的数据量
@property (nonatomic, strong) NSMutableDictionary *dataDic;

@end

//模拟场景,容许多个线程同时访问字典,可是只有一个线程能够写字典
@implementation CustomDictionary {
    //定义一个并发队列
    dispatch_queue_t _concurrent_queue;

}

- (instancetype)init {
    if (self = [super init]) {
        _concurrent_queue = dispatch_queue_create("com.mf.read_write_queue", DISPATCH_QUEUE_CONCURRENT);
        _dataDic = @{}.mutableCopy;
    }
    
    return self;
}

// 读取数据,并发操做
- (id)objectForKey:(NSString *)key {
    __block id obj;
    //同步读取数据
    dispatch_sync(_concurrent_queue, ^{
        obj = [self.dataDic objectForKey:key];
    });
    
    return obj;
    
}

// 写入数据,异步栅栏
- (void)setObject:(id)obj forKey:(NSString *)key {
    //异步栅栏调用设置数据
    dispatch_barrier_async(_concurrent_queue, ^{
        [self.dataDic setObject:obj forKey:key];
    });
}

@end

复制代码

五、iOS多线程中有多少种方式能够作到等待前面线程执行完毕再执行后面的线程?

答:
  • barrier
dispatch_queue_t queue = dispatch_queue_create("com.mf.barrier", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
    NSLog(@"1");
});
    
dispatch_async(queue, ^{
    NSLog(@"2");
});
    
dispatch_barrier_async(queue, ^{
    NSLog(@"等待任务1,2上面执行完毕");
});
    
dispatch_async(queue, ^{
    NSLog(@"3");
});
    
dispatch_async(queue, ^{
    NSLog(@"4");
});
复制代码
  • group notify
// 全局变量group
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 进入组(进入组和离开组必须成对出现, 不然会形成死锁)
dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    NSLog(@"1");
    //离开组
    dispatch_group_leave(group);
});

dispatch_group_enter(group);
dispatch_group_async(group, queue, ^{
    NSLog(@"2");
    dispatch_group_leave(group);
});

dispatch_group_notify(group, queue, ^{  // 监听组里全部线程完成的状况
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        NSLog(@"任务1,2已完成");
    });    
});
复制代码
  • NSOperationQueue Dependency
//建立队列
    NSOperationQueue *queue=[[NSOperationQueue alloc] init];
    //建立操做
    NSBlockOperation *operation1=[NSBlockOperation blockOperationWithBlock:^(){
        NSLog(@"执行第1次操做,线程:%@",[NSThread currentThread]);
    }];
    NSBlockOperation *operation2=[NSBlockOperation blockOperationWithBlock:^(){
        NSLog(@"执行第2次操做,线程:%@",[NSThread currentThread]);
    }];
    NSBlockOperation *operation3=[NSBlockOperation blockOperationWithBlock:^(){
        NSLog(@"执行第3次操做,线程:%@",[NSThread currentThread]);
    }];
    //添加依赖
    [operation1 addDependency:operation2];
    [operation2 addDependency:operation3];
    //将操做添加到队列中去
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
复制代码
  • semaphore
/* 好比说咱们须要请求三张元素图,拼合成一张海报。咱们须要先对元素图进行请求然后才能合成海报,这就造成了依赖关系。咱们经过semaphore限制资源数为3,供请求元素图使用,待请求完成后,释放信号量,便能走到合成的耗时操做。 */

//建立信号量,参数:信号量的初值,若是小于0则会返回NULL
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
    
//元素图1
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    //等待下降信号量
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"请求第1张元素图");
    sleep(1);
    NSLog(@"第1张元素图Get");
    //提升信号量
    dispatch_semaphore_signal(semaphore);
});
    
//元素图2
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"请求第2张元素图");
    sleep(1);
    NSLog(@"第2张元素图Get");
    dispatch_semaphore_signal(semaphore);
});
    
//元素图3
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"请求第3张元素图");
    sleep(1);
    NSLog(@"第3张元素图Get");
    dispatch_semaphore_signal(semaphore);
});
    
//合成海报
dispatch_async(dispatch_get_global_queue(0, 0), ^{
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"合成海报");
    sleep(1);
    NSLog(@"海报Get");
    dispatch_semaphore_signal(semaphore);
});
复制代码

注意:
正常的使用顺序是先下降而后再提升,这两个函数一般成对使用。


仓库

本篇相关代码


联系方式

邮箱: adrenine@163.com

邮箱: holaux@gmail.com

邮箱: ledahapple@icloud.com

相关文章
相关标签/搜索