iOS 多线程之GCD
2016-01-15 20:44
507 查看
1、一个应用是如何在设备上运行的?
编译器会将我们写的代码转化为二进制代码,这些二进制代码就是操作CPU的命令列。一个App包安装到iPhone上,实质就是汇集了这个App要执行的所有CPU命令列和数据的总和安装到iPhone上了。
App启动之后,iOS会根据用户的操作,首先将包含在应用程序中的CPU命令列配置到内存中,接着CPU从应用程序指定的地址开始,一个一个的执行CPU的命令列。在Xcode中下一个断点,进行单步调试,会看到下面的二进制代码。CPU执行命令列总是从pushq开始,执行到popq,整个命令列就结束。
2、什么是iOS的线程?
由于一个CPU一次只能执行一个命令,不能执行某处分开并列的两个命令,因此通过CPU执行的CPU执行列可看成是一条没有分叉的路径,CPU在执行的过程中不会产生分歧。因此,一个完整的没有分叉的CPU执行命令列就是一个线程。
3、什么是多线程?
由线程的本质可以知道,多条完整的没有分歧的CPU执行命令列就是多线程。
4、Apple中多线程的实现
Apple引入了一种叫做”上下文切换“的方式来实现多线程。
OSX 和 iOS的核心XNU内核在发生系统操作事件时,会切换执行路径。例如,从路径A切换到路径B,切换时,系统会将当前路径A的状态,即路径A对应的CPU的寄存器的信息保存到各自路径专用的内存块中;然后,从B路径的路径专用内存块中复原CPU寄存器信息,继续执行B路径的CPU命令列。由于使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此,看起来就像是一个CPU核心能够并列的执行多个线程一样。
5、 GCD是什么?
GCD是iOS中异步执行任务的技术之一,iOS开发中常用的系统级的多线程管理手段。GCD是纯C语言的API,执行速度很快,相比较Objective-C对象 operation,也更轻量级,开销也更小。
6、GCD常用API
补充 :NSTimer在主线程的runloop里会在runloop切换其它模式时停止,这时就需要手动在子线程开启一个模式为NSRunLoopCommonModes的runloop,如果不想开启一个新的runloop可以用不跟runloop关联的dispatch source timer,如下。
简单的使用GCD相关API的Demo
关于锁、dispatch_set_context 和 dispatch_get_context、dispatch_suspend 和 dispatch_resume 使用下篇文章会深入将到。
有讲的不对的地方,请大家指出。
编译器会将我们写的代码转化为二进制代码,这些二进制代码就是操作CPU的命令列。一个App包安装到iPhone上,实质就是汇集了这个App要执行的所有CPU命令列和数据的总和安装到iPhone上了。
App启动之后,iOS会根据用户的操作,首先将包含在应用程序中的CPU命令列配置到内存中,接着CPU从应用程序指定的地址开始,一个一个的执行CPU的命令列。在Xcode中下一个断点,进行单步调试,会看到下面的二进制代码。CPU执行命令列总是从pushq开始,执行到popq,整个命令列就结束。
0x10749daea <+0>: pushq %rbp 0x10749daeb <+1>: movq %rsp, %rbp 0x10749daf7 <+13>: subq $0x1f8, %rsp 0x10749dafe <+20>: movq %rdi, %rbx 0x10749db01 <+23>: movq 0xb9ef38(%rip), %r14 ; (void UIViewController._view 0x10749db16 <+44>: cmpq $0x0, (%rbx,%r13) 0x10749db1b <+49>: jne 0x10749e2af ; <+1989> 0x10749db21 <+55>: movq 0xb24058(%rip), %rsi ; "_isDeallocating" 0x10749db28 <+62>: movq %rbx, %rdi 0x10749db2b <+65>: callq *0xb9f67f(%rip) ; (void *)0x00000001069e6800: objc_msgSend 0x10749db31 <+71>: testb %al, %al 0x10749db33 <+73>: je 0x10749db60 ; <+118> 0x10749db35 <+75>: movb 0xb99e07(%rip), %al ; 0x10749db4a <+96>: xorl %eax, %eax 0x10749db4c <+98>: movq %rbx, %rsi 0x10749db4f <+101>: callq 0x107dafa74 ; symbol stub for: NSLog 0x10749db54 <+106>: movb $0x1, 0xb99de7(%rip) ; 0x10749dcdb <+497>: movq %rbx, %rdi 0x10749e2c5 <+2011>: popq %rbp
2、什么是iOS的线程?
由于一个CPU一次只能执行一个命令,不能执行某处分开并列的两个命令,因此通过CPU执行的CPU执行列可看成是一条没有分叉的路径,CPU在执行的过程中不会产生分歧。因此,一个完整的没有分叉的CPU执行命令列就是一个线程。
3、什么是多线程?
由线程的本质可以知道,多条完整的没有分歧的CPU执行命令列就是多线程。
4、Apple中多线程的实现
Apple引入了一种叫做”上下文切换“的方式来实现多线程。
OSX 和 iOS的核心XNU内核在发生系统操作事件时,会切换执行路径。例如,从路径A切换到路径B,切换时,系统会将当前路径A的状态,即路径A对应的CPU的寄存器的信息保存到各自路径专用的内存块中;然后,从B路径的路径专用内存块中复原CPU寄存器信息,继续执行B路径的CPU命令列。由于使用多线程的程序可以在某个线程和其他线程之间反复多次进行上下文切换,因此,看起来就像是一个CPU核心能够并列的执行多个线程一样。
5、 GCD是什么?
GCD是iOS中异步执行任务的技术之一,iOS开发中常用的系统级的多线程管理手段。GCD是纯C语言的API,执行速度很快,相比较Objective-C对象 operation,也更轻量级,开销也更小。
6、GCD常用API
1、队列 全局队列,一个并行的队列 dispatch_get_global_queue 主队列,主线程中的唯一队列,一个串行队列 dispatch_get_main_queue 2、自定义队列 串行队列 dispatch_queue_create("serialqueue",DISPATCH_QUEUE_SERIAL); 并行队列 dispatch_queue_create("serialqueue",DISPATCH_QUEUE_CONCURRENT); 3、同步异步线程创建 同步线程 dispatch_sync(..., ^(block)) 异步线程 dispatch_asyns(..., ^(block)) 4、给多个并发线程添加障碍物 barrier 解决多线程并发读写同一个资源发生死锁 dispatch_barrier_async(queue, ^{}); 5、快速迭代,避免线程爆炸应该使用 dispatch_apply dispatch_apply(count,queue, ^(size_t i){}); 6、dispatch groups 是专门用来监视多个异步任务,使用dispatch_group_notify异步执行多个任务的汇总。 使用dispatch_group_wait容易造成线程阻塞,不建议使用。 创建一个group dispatch_group_t = dispatch_group_create(); dispatch_group_async(group, queue, ^{}); 汇总通知,这个是异步的 dispatch_group_notify(gruoup, queue), ^{}); 同步线程,可能会发生线程阻塞 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 7、dispatch block 队列的执行任务都是block的方式 创建一个block dispatch_block_t block = dispatch_block_create(0, ^{....}); dispatch_async(queue, block); // QOS Way dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{...}); dispatch_async(queue, qosBlock); 8、Dispatch IO文件的操作 ,多个线程去读文件的切片数据,对于大的数据文件这样就会比单线程快好多。 dispatch_async(queue,^{ “ read 0-99 bytes ”}); dispatch_async(queue,^{ “ read 100-199 bytes ”}); dispatch_async(queue,^{ “ read 200-299 bytes ”}); 9、Dispatch Semaphore 信号量 保证线程同步的方法。使用dispatch_semaphore_signal加1 dispatch_semaphore_wait减1,为0时等待的设置方式来达到线程同步的目的和同步锁一样能够解决资源抢占的问题。 dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_semaphore_signal(semaphore); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); 10、Dispatch Source dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, queue); dispatch_resume(source); dispatch_source_merge_data(source, 1); 11、锁 NSRecursiveLock: 递归锁 NSDistributeLock:分布锁 NSConditionLock: 条件所 OSSpinLock: 自旋锁 pthread_mutex_t: 同步锁基于C语言,底层api性能高,使用方法和其它的类似 @synchroinized: 更加简单 12、dispatch_set_context 和 dispatch_get_context 13、dispatch_suspend 和 dispatch_resume 挂起和恢复队列
补充 :NSTimer在主线程的runloop里会在runloop切换其它模式时停止,这时就需要手动在子线程开启一个模式为NSRunLoopCommonModes的runloop,如果不想开启一个新的runloop可以用不跟runloop关联的dispatch source timer,如下。
dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0, 0, DISPATCH_TARGET_QUEUE_DEFAULT); dispatch_source_set_event_handler(source, ^(){ NSLog(@"Time flies."); }); dispatch_time_t start dispatch_source_set_timer(source, DISPATCH_TIME_NOW, 5ull * NSEC_PER_SEC,100ull * NSEC_PER_MSEC); self.source = source; dispatch_resume(self.source);
简单的使用GCD相关API的Demo
- (void)moveFileWithApply{ NSString *fromPath = @"/Users/HW/Desktop/speedTestApp"; NSString *toPath = @"/Users/HW/Desktop/book"; NSFileManager *manager= [NSFileManager defaultManager]; NSArray *subPaths = [manager subpathsOfDirectoryAtPath:fromPath error:nil]; dispatch_queue_t queue = dispatch_get_global_queue(0, 0); NSInteger count = subPaths.count; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ dispatch_apply(count, queue, ^(size_t index) { NSString *subpath = subPaths[index]; NSLog(@"%@ == ",subpath); NSString *fromFullPath = [fromPath stringByAppendingPathComponent:subpath]; NSLog(@"fileFullPath == %@# == ",fromFullPath); NSString *toFullFilePath = [toPath stringByAppendingPathComponent:subpath]; [manager moveItemAtPath:fromPath toPath:toFullFilePath error:nil]; }); dispatch_async(dispatch_get_main_queue(), ^{ NSLog(@"所有的文件都已经移动完毕,回到主线程"); }); }); } - (void)dipatchCombie{ dispatch_group_t group = dispatch_group_create(); dispatch_queue_t queue = dispatch_get_global_queue(0, 0); // 异步下载图片一 dispatch_group_async(group, queue, ^{ NSURL *url = [NSURL URLWithString:@"http://pic14.nipic.com/20110522/7411759_164157418126_2.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; image1 = [UIImage imageWithData:data]; }); // 异步下载图片2 dispatch_group_async(group, queue, ^{ NSURL *url = [NSURL URLWithString:@"http://pic28.nipic.com/20130402/9252150_190139450381_2.jpg"]; NSData *data = [NSData dataWithContentsOfURL:url]; image2 = [UIImage imageWithData:data]; }); // 拦截通知,回到主线程去合成图片 dispatch_group_notify(group, dispatch_get_main_queue(), ^{ //绘制图片 UIGraphicsBeginImageContext(CGSizeMake(240, 240)); [image1 drawInRect:CGRectMake(0, 0, 240, 120)]; [image2 drawInRect:CGRectMake(0, 120, 240, 120)]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); UIImageView *iv = [[UIImageView alloc] initWithFrame:CGRectMake(20, 100, 240, 240)]; iv.image = image; imageView = iv; [self.view addSubview:iv]; }); } - (void)barrier{ dispatch_queue_t queue = dispatch_queue_create("12312311",DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:2.f]; NSLog(@"----1 --------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"-----2 ---------%@",[NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ NSLog(@"------barrier---------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ [NSThread sleepForTimeInterval:1.f]; NSLog(@"-------3--------%@",[NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"--------4--------%@",[NSThread currentThread]); }); } - (void)dispatchAfter{ double delayInSeconds = 2.0; dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC)); dispatch_after(popTime, dispatch_get_main_queue(), ^{ NSLog(@"主线程延迟2秒后执行"); }); } - (void)dealWithMaybeExplode:(BOOL)explode { dispatch_queue_t queue = dispatch_queue_create("explode",DISPATCH_QUEUE_CONCURRENT); if (explode) { for (int i = 0; i < 9999; i++) { dispatch_async(queue, ^{ NSLog(@"wrong %d ",i); }); } }else{ dispatch_apply(9999, queue, ^(size_t i) { NSLog(@"right %zu ",i); }); } } - (void)dispatchGroupWaitDemo{ dispatch_queue_t queue = dispatch_queue_create("000", DISPATCH_QUEUE_CONCURRENT); dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, queue, ^{ [NSThread sleepForTimeInterval:2.f]; NSLog(@"1"); }); dispatch_group_async(group, queue, ^{ NSLog(@"2"); }); dispatch_group_wait(group, DISPATCH_TIME_FOREVER); NSLog(@"等待结束"); } - (void)createDispatchBlock{ dispatch_queue_t queue = dispatch_queue_create("block", DISPATCH_QUEUE_CONCURRENT); dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"run block"); }); dispatch_async(queue, block); // QOS Way dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_USER_INITIATED, -1, ^{ NSLog(@" run qos block'"); }); dispatch_async(queue, qosBlock); } - (void)dispatchSemaphoreDemo{ dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ NSLog(@"start'"); [NSThread sleepForTimeInterval:1.f]; NSLog(@"semaphore + 1"); dispatch_semaphore_signal(semaphore); }); dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); NSLog(@"信号使用结束"); } - (void)deadLockCase1{ NSLog(@"1"); // 主队列的同步线程,按照FIFO的原则 (先入先出),2排在3后面会等3执行完,但因为是同步线程,3又要等待2执行完,所以造成死锁 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"3"); } - (void)deadLockCase2 { NSLog(@"1"); // 3会等2,因为2在全局并行队列里面,不需要等3,这样2执行完回到主队列,3就开始执行 dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),^{ NSLog(@"2"); [NSThread sleepForTimeInterval:2.f]; }); NSLog(@"3"); } - (void)deadLockCase3 { dispatch_queue_t serialQueue = dispatch_queue_create("deadLock", DISPATCH_QUEUE_SERIAL); NSLog(@"1"); dispatch_async(serialQueue, ^{ NSLog(@"2"); // 串行队列里面同步一个串行队列就会死锁 dispatch_sync(serialQueue, ^{ NSLog(@"3"); }); // dispatch_async(serialQueue, ^{ // NSLog(@"3"); // }); NSLog(@"4"); }); NSLog(@"5"); // 解决方法: dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); dispatch_sync(serialQueue, ^{ NSLog(@"3"); }); NSLog(@"4"); }); NSLog(@"5"); } - (void)deadLockCase4 { NSLog(@"1"); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"2"); // 将同步的串行队列放到另外一个线程就能解决Case3的死锁 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"3"); }); NSLog(@"4"); }); NSLog(@"5"); } - (void)deadLockCase5 { dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"1"); // 回到主线程,主线程在做while 死循环,后面的操作就没有办法做了。 dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"2"); }); NSLog(@"3"); }); NSLog(@"4"); // 死循环 while (1) {} } - (void)dispatchSource{ dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_global_queue(0, 0)); dispatch_source_set_event_handler(source, ^{ dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"更新UI"); }); }); dispatch_resume(source); dispatch_async(dispatch_get_global_queue(0, 0), ^{ NSLog(@"后台加载"); // 通知队列 dispatch_source_merge_data(source, 1); }); }
关于锁、dispatch_set_context 和 dispatch_get_context、dispatch_suspend 和 dispatch_resume 使用下篇文章会深入将到。
有讲的不对的地方,请大家指出。
相关文章推荐
- Python3写爬虫(四)多线程实现数据爬取
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 峰回路转,Firefox 浏览器即将重返 iOS 平台
- 不可修补的 iOS 漏洞可能导致 iPhone 4s 到 iPhone X 永久越狱
- iOS 12.4 系统遭黑客破解,漏洞危及数百万用户
- 每日安全资讯:NSO,一家专业入侵 iPhone 的神秘公司
- [转][源代码]Comex公布JailbreakMe 3.0源代码
- C#实现多线程的同步方法实例分析
- 浅谈chuck-lua中的多线程
- C#简单多线程同步和优先权用法实例
- C#多线程学习之(四)使用线程池进行多线程的自动管理
- C#多线程编程中的锁系统(三)
- C#多线程学习之(六)互斥对象用法实例
- 基于一个应用程序多线程误用的分析详解
- C#多线程学习之(三)生产者和消费者用法分析
- C#多线程学习之(一)多线程的相关概念分析
- C#多线程之Thread中Thread.IsAlive属性用法分析
- 分享我在工作中遇到的多线程下导致RCW无法释放的问题
- C#多线程编程之使用ReaderWriterLock类实现多用户读与单用户写同步的方法