多线程

48 阅读3分钟
  1. GCD
  2. NSOperation
  3. NSThread
  4. 多线程与锁

GCD

  • 同步/异步 和 串行/并发
  • dispatch_barrier_async(多读单写)
  • dispatch_group

同步/异步 和 串行/并发

  • dispatch_sync(serial_queue,^{//任务});
  • dispatch_async(serial_queue,^{//任务});
  • dispatch_sync(concurrent_queue,^{//任务});
  • dispatch_async(concurrent_queue,^{//任务});

同步串行

- (void)viewDidLoad {
dispatch_sync(dispatch_get_main_queue(),^{
//任务
[self doSomething];
});
}

产生死锁! 队列引起的循环等待

image.png

- (void)viewDidLoad {
dispatch_sync(serialQueue,^{
//任务
[self doSomething];
});
}

正常运行

image.png

同步并发

- (void)viewDidLoad {
NSLog(@"1");
dispatch_sync(global_queue,^{
NSLog(@"2");
   dispatch_sync(global_queue,^{
NSLog(@"3");
});
NSLog(@"4");
});
NSLog(@"5");
}

输出结果:12345;

异步串行

- (void)viewDidLoad {
dispatch_sync(global_queue,^{
   [self doSomething];
});
}

异步并发

- (void)viewDidLoad {
dispatch_sync(global_queue,^{
NSLog(@"1");
   [self performSelector:@selector(printLog) withObject:nil afterDelay:0];
   NSLog(@"3");
});
}

- (void)printLog{NSLog(@"2");};

输出结果:13;不会输出结果2;

dispatch_barrier_async() 怎样利用GCD实现多读单写?

  • 读者、读者并发
  • 读者、写者互斥
  • 写者、写者互斥

image.png 多读单写实现流程

image.png dispatch_barrier_async(concurrent_queue,^{//写操作});

dispatch_group_async() 使用GCD实现这个需求:A、B、C三个任务并发,完成后执行任务D?

image.png

多图上传,可变数据接受,然后,替换数组中的内容。

- (void)handle
{
 dispatch_group_t group = dispatch_group_create();
 for (NSURL *url in arrayURLs){
   dispatch_group_async(group,concurrent_queue,^{
   NSLog(@"url is %@",url);
   }):
 }
 dispatch_group_notify(group,dispatch_get_main_queue(),^{
 NSLog(@"所有图片下载完成");
 });
}

NSOperation

需要和NSOperationQueue配合使用来实现多线程方案

  • 添加任务依赖
  • 任务执行状态控制
  • 最大并发量

任务执行状态控制

  • isReady
  • isExecuting
  • isFinished
  • isCancelled

状态控制 如果只重写main方法,底层控制变更任务执行完成状态,以及任务退出。 如果重写了start方法,自行控制任务状态

源码(gnustep-base-1.24.9) 状态控制 系统是怎样移除一个isFinished=YES的NSOperation的? 答案:通过KVO。

NSThread

启动流程

image.png 创建runloop实现常驻线程。

start方法 源码(gnustep-base-1.24.9)

多线程与锁

iOS当中都有哪些锁?

  • @synchronized
  • atomic
  • OSSpinLock
  • NSRecursiveLock
  • NSLock
  • dispatch_semaphore_t

synchronized 一般在创建单例对象的时候使用,保证多线程下创建对象是唯一的

atomin 修饰属性关键字 对被修饰的对象进行原子操作(不负责使用) @property(atomic) NSMutableArray *array; self.array = [NSMutableArray array];(线程安全) [self.array addObject:obj];(线程不安全)

OSSpinLock 循环等待询问,不释放当前资源 用于轻量级数据访问,简单的int值+1/-1操作 (runtime的源码回答自旋锁)

NSLock

- (void)methodA{
[lock lock];
[self methodB];
[lock unlock];
}
- (void)methodB{
[lock lock];
//操作逻辑
[lock unlock];
}

以上会死锁,可以通过NSRecursiveLock解决

- (void)methodA{
[recursiveLock lock];
[self methodB];
[recursiveLock unlock];
}
- (void)methodB{
[recursiveLock lock];
//操作逻辑
[recursiveLock unlock];
}

递归锁可以重入,防止死锁。

dispatch_semaphore_t(信号量)

  • dispatch_semaphore_create(1)
  • dispatch_semaphore_wait(semaphore,DISPATCH_TIME_FOREVER);
  • dispatch_semaphore_signal(semaphore);

源码解析 dispatch_semaphore_create()

struct semaphore {
  int value;
  List`<thread>`;
}

dispatch_semaphore_wait()

{
S.value = S.value - 1;
if S.value <0 then Block(S.list); =>阻塞是一个主动行为。
}

dispatch_semaphore_signal()

S.value = S.value + 1;
if S.value <= 0 then wakeup(S.List); =>唤醒是一个被动行为

多线程总结

  • 怎样用GCD实现多读单写?
  • iOS系统为我们提供了几种多线程的技术各自的特点是怎样的?
  • NSOperation对象在Finished之后是怎样从queue当中移除掉的?
  • 你都用过哪些锁?结合实际谈谈你是怎样使用的?