柚子快报怎么转码778899分享:iOS - 多线程-GCD

http://yzkb.51969.com/

文章目录

iOS - 多线程-GCD1. 常见多线程方案2. GCD2.1 GCD的常见函数GCD中有2个用来执行任务的函数

2.2 GCD的队列2.2.1 GCD的队列可以分为2大类型

2.3 容易混淆的术语2.4.1 有4个术语比较容易混淆:`同步、异步`、`并发、串行`

2.4 各种队列的执行效果

3. 死锁3.1 死锁示例3.2 死锁分析3.3 其他示例3.3.1 interview023.3.2 interview033.3.3 interview04

4. 案例4.1 案例14.2 分析

5. 拓展GNUstep

iOS - 多线程-GCD

1. 常见多线程方案

NSThread、GCD、NSOperation底层都依赖于pthread

2. GCD

2.1 GCD的常见函数

GCD中有2个用来执行任务的函数

用同步的方式执行任务 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

queue:队列block:任务 用异步的方式执行任务 dispatch_async(dispatch_queue_t queue, dispatch_block_t block); GCD源码:https://github.com/apple/swift-corelibs-libdispatch

2.2 GCD的队列

2.2.1 GCD的队列可以分为2大类型

并发队列(Concurrent Dispatch Queue)

可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)并发功能只有在异步(dispatch_async)函数下才有效 串行队列(Serial Dispatch Queue)

让任务一个接着一个地执行(一个任务执行完毕后,再执行下一个任务)

2.3 容易混淆的术语

2.4.1 有4个术语比较容易混淆:同步、异步、并发、串行

同步和异步主要影响:能不能开启新的线程

同步:在当前线程中执行任务,不具备开启新线程的能力

异步:在新的线程中执行任务,具备开启新线程的能力 并发和串行主要影响:任务的执行方式

并发:多个任务并发(同时)执行

串行:一个任务执行完毕后,再执行下一个任务

2.4 各种队列的执行效果

使用sync函数往当前串行队列中添加任务,会卡住当前的串行队列(产生死锁)

3. 死锁

3.1 死锁示例

// 问题:以下代码是在主线程执行的,会不会产生死锁?

NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_sync(queue, ^{

NSLog(@"执行任务2");

});

NSLog(@"执行任务3");

3.2 死锁分析

如上代码

正常情况应该是顺序执行任务1、任务2、任务3任务2使用dispatch_sync同步执行方式,放入主线程队列,因此任务2需要排队等待前面的任务执行完成后才执行但是当前方法体viewDidLoad可以认为就是一个任务在执行,但是执行到任务2的dispatch_sync处,会等待dispatch_sync执行完成再继续往下执行此时,相当于任务2等待当前执行任务执行完成,当前执行任务也在等待任务2执行完成,相互等待因此造成线程死锁

3.3 其他示例

3.3.1 interview02

- (void)interview02 {

// 问题:interview01中的sync,改成 async。会不会产生死锁?不会!

/* 打印日志:

执行任务1

执行任务3

执行任务2

*/

NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_async(queue, ^{

NSLog(@"执行任务2");

});

NSLog(@"执行任务3");

}

3.3.2 interview03

- (void)interview03 {

// 会不会产生死锁?会!

/*

分析:

执行任务2 后,同步等待任务2 执行,但是因为queue是串行的,所以会相互等待

*/

NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_SERIAL);

dispatch_async(queue, ^{

NSLog(@"执行任务2");

dispatch_sync(queue, ^{

NSLog(@"执行任务3");

});

NSLog(@"执行任务4");

});

NSLog(@"执行任务5");

}

3.3.3 interview04

- (void)interview04 {

// interview03改为并发队列(DISPATCH_QUEUE_CONCURRENT),会不会死锁?不会!

NSLog(@"执行任务1");

dispatch_queue_t queue = dispatch_queue_create("myqueue", DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

NSLog(@"执行任务2");

dispatch_sync(queue, ^{

NSLog(@"执行任务3");

});

NSLog(@"执行任务4");

});

NSLog(@"执行任务5");

}

4. 案例

4.1 案例1

如下代码打印什么:

- (void)test {

NSLog(@"2");

}

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

dispatch_async(queue, ^{

NSLog(@"1");

[self performSelector:@selector(test) withObject:nil afterDelay:0];

NSLog(@"3");

});

}

打印结果: 观察到,2不会打印

去掉dispatch_async又会怎么样

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

NSLog(@"1");

[self performSelector:@selector(test) withObject:nil afterDelay:0];

NSLog(@"3");

}

这时候都有打印,只不过打印顺序1>3>2

接着,回到dispatch_async里执行,但是把afterDelay去掉

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {

dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

dispatch_async(queue, ^{

NSLog(@"1");

[self performSelector:@selector(test) withObject:nil];

NSLog(@"3");

});

}

打印结果是1>2>3,这次打印顺序是正常的

4.2 分析

上面的例子中,主要是考察runloop与多线程的相关知识

首先,在dispatch_async中使用- (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay;方法来执行,使用afterDelay:0看似是在没有延迟的情况下执行,实际上因为该方法是基于 runloop的,相当于往runloop添加一个定时器,但是因为此时我们是在子线程中执行的,子线程中的runloop默认不会开启,所以test方法没有执行。我们尝试开启runloop

dispatch_async(queue, ^{

NSLog(@"1");

[self performSelector:@selector(test) withObject:nil afterDelay:0];

NSLog(@"3");

[[NSRunLoop currentRunLoop] addPort:[[NSPort alloc]init] forMode:NSDefaultRunLoopMode];

[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

});

可以看到,2打印了

接着,我们去掉了dispatch_async,发现打印结果是1>3>2,为什么3会比2先打印的? 使用afterDelay:0看似是在没有延迟的情况下执行,实际上因为该方法是基于 runloop的定时器,虽然没有延迟设置为 0,但是runloop的定时器是在被唤醒的时候处理定时器的,但是在进入休眠之前会处理完点击事件,因此看到的打印结果是1、3先打印,然后打印2 最后,回到dispatch_async中执行,只不过使用的是- (id)performSelector:(SEL)aSelector withObject:(id)object;方法来执行,查看源码 该方法实际是直接使用objc_msgSend方法执行,相当于我们直接[self test]这样调用方法,所以这时候打印顺序是正常的1>2>3

5. 拓展

GNUstep

GNUstep是GNU计划的项目之一,它将Cocoa的OC库重新开源实现了一遍

源码地址:https://gnustep.github.io/resources/downloads.html

虽然GNUstep不是苹果官方源码,但还是具有一定的参考价值

@oubijiexi

柚子快报怎么转码778899分享:iOS - 多线程-GCD

http://yzkb.51969.com/

好文链接

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。