iOS中多线程编程

转载此文作为笔记:

iOS中多线程编程工具主要有:

  • NSThread
  • NSOperation
  • GCD

这三种方法都简单易用,各有千秋.但无疑GCD是最有诱惑力的,因为其本身是apple为多核的并行运算提出的解决方案.虽然当前移动平台用双核的不多,但不影响GCD作为多线程编程的利器(ipad2已经是双核了,这无疑是一个趋势).

http://www.cnblogs.com/scorpiozj/archive/2011/07/25/2116459.html

GCD是和block紧密相连的,所以最好先了解下block(可以查看这里).GCD是C level的函数,这意味着它也提供了C的函数指针作为参数,方便了C程序员.

一、下面首先来看GCD的使用:

dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

async表明异步运行,block代表的是你要做的事情,queue则是你把任务交给谁来处理了.(除了async,还有sync,delay,本文以async为例).

之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.GCD里就有三种queue来处理.

先来介绍一下 Main queue:

  顾名思义,运行在主线程,由dispatch_get_main_queue获得.和ui相关的就要使用Main Queue.

  1. //GCD下载图片刷新主界面的例子
  2. /*
  3. - (IBAction)touchUpInsideByThreadOne:(id)sender {
  4. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  5. NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
  6. NSData * data = [[NSData alloc]initWithContentsOfURL:url];
  7. UIImage *image = [[UIImage alloc]initWithData:data];
  8. if (data != nil) {
  9. dispatch_async(dispatch_get_main_queue(), ^{
  10. self.imageView.image = image;
  11. });
  12. }
  13. });
  14. }*/

通过与线程池的配合,dispatch queue分为下面两种:而系统默认就有一个串行队列main_queue和并行队列global_queue:

  • Serial Dispatch Queue -- 线程池只提供一个线程用来执行任务,所以后一个任务必须等到前一个任务执行结束才能开始。
  • Concurrent Dispatch Queue -- 线程池提供多个线程来执行任务,所以可以按序启动多个任务并发执行。

而系统默认就有一个串行队列main_queue和并行队列global_queue:

 

  1. dispatch_queue_t globalQ = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  2. dispatch_queue_t mainQ = dispatch_get_main_queue();

通常,我们可以在global_queue中做一些long-running的任务,完成后在main_queue中更新UI,避免UI阻塞,无法响应用户操作:


  1. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  2. // long-running task
  3. dispatch_async(dispatch_get_main_queue(), ^{
  4. // update UI
  5. });
  6. });

1.Serial quque(private dispatch queue)

  每次运行一个任务,可以添加多个,执行次序FIFO. 通常是指程序员生成的,比如:

NSDate *da = [NSDate date];
NSString *daStr = [da description];
const char *queueName = [daStr UTF8String];
dispatch_queue_t myQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_PRIORITY_DEFAULT);
下面还是下载图片例子:
  1. - (IBAction)touchUpInsideByThreadOne:(id)sender {
  2. NSDate *da = [NSDate date];
  3. NSString *daStr = [da description];
  4. const char *queueName = [daStr UTF8String];
  5. dispatch_queue_t myQueue = dispatch_queue_create(queueName, NULL);
  6. dispatch_async(myQueue, ^{
  7. NSURL * url = [NSURL URLWithString:@"http://avatar.csdn.net/2/C/D/1_totogo2010.jpg"];
  8. NSData * data = [[NSData alloc]initWithContentsOfURL:url];
  9. UIImage *image = [[UIImage alloc]initWithData:data];
  10. if (data != nil) {
  11. dispatch_async(dispatch_get_main_queue(), ^{
  12. self.imageView.image = image;
  13. });
  14. }
  15. });
  16. dispatch_release(myQueue);
  17. }

为了验证Serial queue的FIFO特性,写了如下的验证代码:发现的确是顺序执行的。

  1. - (IBAction)touchUpInsideByThreadOne:(id)sender {
  2. NSDate *da = [NSDate date];
  3. NSString *daStr = [da description];
  4. const char *queueName = [daStr UTF8String];
  5. dispatch_queue_t myQueue = dispatch_queue_create(queueName, DISPATCH_QUEUE_SERIAL);
  6. dispatch_async(myQueue, ^{
  7. [NSThread sleepForTimeInterval:6];
  8. NSLog(@"[NSThread sleepForTimeInterval:6];");
  9. });
  10. dispatch_async(myQueue, ^{
  11. [NSThread sleepForTimeInterval:3];
  12. NSLog(@"[NSThread sleepForTimeInterval:3];");
  13. });
  14. dispatch_async(myQueue, ^{
  15. [NSThread sleepForTimeInterval:1];
  16. NSLog(@"[NSThread sleepForTimeInterval:1];");
  17. });
  18. dispatch_release(myQueue);
  19. }

运行结果为:

  1. 2013-07-24 16:37:14.397 NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:6];
  2. 2013-07-24 16:37:17.399 NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:3];
  3. 2013-07-24 16:37:18.401 NSThreadAndBlockDemo[1924:12303] [NSThread sleepForTimeInterval:1];

3. Concurrent queue(global dispatch queue):

可以同时运行多个任务,每个任务的启动时间是按照加入queue的顺序,结束的顺序依赖各自的任务.使用dispatch_get_global_queue获得.

  1. - (IBAction)touchUpInsideByThreadOne:(id)sender {
  2. dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  3. dispatch_async(myQueue, ^{
  4. [NSThread sleepForTimeInterval:6];
  5. NSLog(@"[NSThread sleepForTimeInterval:6];");
  6. });
  7. dispatch_async(myQueue, ^{
  8. [NSThread sleepForTimeInterval:3];
  9. NSLog(@"[NSThread sleepForTimeInterval:3];");
  10. });
  11. dispatch_async(myQueue, ^{
  12. [NSThread sleepForTimeInterval:1];
  13. NSLog(@"[NSThread sleepForTimeInterval:1];");
  14. });
  15. dispatch_release(myQueue);
  16. }

运行的结果为:

  1. 2013-07-24 16:38:41.660 NSThreadAndBlockDemo[1944:12e03] [NSThread sleepForTimeInterval:1];
  2. 2013-07-24 16:38:43.660 NSThreadAndBlockDemo[1944:12b03] [NSThread sleepForTimeInterval:3];
  3. 2013-07-24 16:38:46.660 NSThreadAndBlockDemo[1944:12303] [NSThread sleepForTimeInterval:6];

二、dispatch_group_async的使用

dispatch_group_async可以实现监听一组任务是否完成,完成后得到通知执行其他的操作。这个方法很有用,比如你执行三个下载任务,当三个任务都下载完成后你才通知界面说完成的了。下面是一段例子代码:

  1. - (IBAction)touchUpInsideByThreadOne:(id)sender {
  2. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  3. dispatch_group_t group = dispatch_group_create();
  4. dispatch_group_async(group, queue, ^{
  5. [NSThread sleepForTimeInterval:6];
  6. NSLog(@"group1 [NSThread sleepForTimeInterval:6];");
  7. });
  8. dispatch_group_async(group, queue, ^{
  9. [NSThread sleepForTimeInterval:3];
  10. NSLog(@"group2 [NSThread sleepForTimeInterval:3];");
  11. });
  12. dispatch_group_async(group, queue, ^{
  13. [NSThread sleepForTimeInterval:1];
  14. NSLog(@"group3 [NSThread sleepForTimeInterval:1];");
  15. });
  16. dispatch_group_notify(group, dispatch_get_main_queue(), ^{
  17. NSLog(@"main thread.");
  18. });
  19. dispatch_release(group);
  20. }

执行结果为:

  1. 2013-07-24 16:48:23.063 NSThreadAndBlockDemo[2004:12e03] group3 [NSThread sleepForTimeInterval:1];
  2. 2013-07-24 16:48:25.063 NSThreadAndBlockDemo[2004:12b03] group2 [NSThread sleepForTimeInterval:3];
  3. 2013-07-24 16:48:28.063 NSThreadAndBlockDemo[2004:12303] group1 [NSThread sleepForTimeInterval:6];
  4. 2013-07-24 16:48:28.065 NSThreadAndBlockDemo[2004:11303] main thread.

果然,dispatch_group_async只会监听最终的结果完成后,并通知main queue,那如果是我们需要顺序执行的话呢?请看下面的dispatch_barrier_async。

3、dispatch_barrier_async的使用

dispatch_barrier_async是在前面的任务执行结束后它才执行,而且它后面的任务等它执行完成之后才会执行

例子代码如下:

  1. - (IBAction)touchUpInsideByThreadOne:(id)sender {
  2. dispatch_queue_t queue = dispatch_queue_create("gcdtest.rongfzh.yc", DISPATCH_QUEUE_CONCURRENT);
  3. dispatch_async(queue, ^{
  4. [NSThread sleepForTimeInterval:3];
  5. NSLog(@"dispatch_async1");
  6. });
  7. dispatch_async(queue, ^{
  8. [NSThread sleepForTimeInterval:1];
  9. NSLog(@"dispatch_async2");
  10. });
  11. dispatch_barrier_async(queue, ^{
  12. NSLog(@"dispatch_barrier_async");
  13. [NSThread sleepForTimeInterval:0.5];
  14. });
  15. dispatch_async(queue, ^{
  16. [NSThread sleepForTimeInterval:1];
  17. NSLog(@"dispatch_async3");
  18. });
  19. }

执行结果为:

  1. 2013-07-24 17:01:54.580 NSThreadAndBlockDemo[2153:12b03] dispatch_async2
  2. 2013-07-24 17:01:56.580 NSThreadAndBlockDemo[2153:12303] dispatch_async1
  3. 2013-07-24 17:01:56.580 NSThreadAndBlockDemo[2153:12303] dispatch_barrier_async
  4. 2013-07-24 17:01:58.083 NSThreadAndBlockDemo[2153:12303] dispatch_async3

如果使用dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);会发现运行结果为:

  1. 2013-07-24 17:07:17.577 NSThreadAndBlockDemo[2247:12e03] dispatch_barrier_async
  2. 2013-07-24 17:07:18.579 NSThreadAndBlockDemo[2247:15207] dispatch_async3
  3. 2013-07-24 17:07:19.578 NSThreadAndBlockDemo[2247:12b03] dispatch_async2
  4. 2013-07-24 17:07:20.577 NSThreadAndBlockDemo[2247:12303] dispatch_async1

说明dispatch_barrier_async的顺序执行还是依赖queue的类型啊,必需要queue的类型为dispatch_queue_create创建的,而且attr参数值必需是DISPATCH_QUEUE_CONCURRENT类型,前面两个非dispatch_barrier_async的类型的执行是依赖其本身的执行时间的,如果attr如果是DISPATCH_QUEUE_SERIAL时,那就完全是符合Serial queue的FIFO特征了。

4、dispatch_apply

执行某个代码片段N次。

dispatch_apply(5, globalQ, ^(size_t index) {

// 执行5次

});

5、dispatch_once

dispatch_once这个函数,它可以保证整个应用程序生命周期中某段代码只被执行一次

  1. static dispatch_once_t onceToken;
  2. dispatch_once(&onceToken, ^{
  3. // code to be executed once
  4. });

6、dispatch_after

有时候我们需要等个几秒钟然后做个动画或者给个提示,这时候可以用dispatch_after这个函数:


  1. double delayInSeconds = 2.0;
  2. dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, delayInSeconds * NSEC_PER_SEC);
  3. dispatch_after(popTime, dispatch_get_main_queue(), ^(void){
  4. // code to be executed on the main queue after delay
  5. });

7、dispatch_set_target_queue

通过dispatch_set_target_queue函数可以设置一个dispatch queue的优先级,或者指定一个dispatch source相应的事件处理提交到哪个queue上。

  1. dispatch_set_target_queue(serialQ, globalQ);

由此可见,GCD的使用非常简单,以我的使用经验来看,以后会逐步淘汰使用NSOperation而改用GCD.

时间: 2024-08-19 10:17:47

iOS中多线程编程的相关文章

iOS中多线程原理与runloop介绍

http://mobile.51cto.com/iphone-403490.htm iOS中多线程原理与runloop介绍 iPhone中的线程应用并不是无节制的,官方给出的资料显示iPhone OS下的主线程的堆栈大小是1M,第二个线程开始都是512KB.并且该值不能通过编译器开关或线程API函数来更改.只有主线程有直接修改UI的能力.……>>详细 兄弟专题:iOS人机交互指南之UI设计基础 1 iOS多线程编程知多少 在iOS的世界里有两种实现多线程的方式: 多线程是一个比较轻量级的方法来

Android中多线程编程(四)AsyncTask类的详细解释(附源码)

Android中多线程编程中AsyncTask类的详细解释 1.Android单线程模型 2.耗时操作放在非主线程中执行 Android主线程和子线程之间的通信封装类:AsyncTask类 1.子线程中更新UI 2.封装.简化异步操作. 3.AsyncTask机制:底层是通过线程池来工作的,当一个线程没有执行完毕,后边的线程是无法执行的.必须等前边的线程执行完毕后,后边的线程才能执行. AsyncTask类使用注意事项: 1.在UI线程中创建AsyncTask的实例 2.必须在UI线程中调用As

iOS中多线程基本概念

进程与线程 什么是进程? 近程是指在系统中正在运行的一个应用程序. 每个近程之间是独立的,每个近程均运行在其专用且受保护的内存空间内. **可以通过“活动监视器”可以查看Mac系统中所有开启的进程. 什么是线程? 一个进程要想执行任务,必须得有线程(每一个进程至少要有一条线程). 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行. 线程的串行 一个线程中任务的执行是串行(顺序执行的) 如果要在1个线程中执行多个任务,那么只能一个一个地按顺序执行这些任务. 也就是说,在同一时间内

IOS开发——多线程编程

1."省电,流畅,优质应用,响应速度快,用户体验好--"也许是众多用户眼中的苹果系统. 2.在众手机商拼CPU主频,拼4核,8核的年代,苹果依然坚持双核,iphone用户体验仍然坚挺. 以上两点IOS是如何优化,在续航,流畅度和响应速度上完胜安卓,答案就是多线程&RunLoop... RunLoop是IOS事件响应与任务处理最核心机制,它贯穿IOS整个系统运作. RunLoop不像一般的线程循环等待任务,传统的线程循环等待任务会导致CPU时间被占用,虽然你设置了睡眠时间,但很多

iOS中多线程的实现方案

我去, 好蛋疼, 刚刚写好的博客就因为手贱在触控板上右划了一下, 写的全丢了, 还得重新写, 博客园就没有针对这种情况的解决方案吗?都不想写了 一. iOS中多线程的实现方案有四种 (1) NSThread陷阱非常多, 有缺陷, 不过是OC的, 偶尔用一下 (2) GCD在苹果在iOS4推出的, 能充分利用设备的多核, 而且不用考虑线程, 性能比NSThread好的多 GCD研究起来就比较深了, 所以在面试的时候会经常被问到 (3) NSOperation封装了很多使用的使用的功能, 某些情况下

iOS中多线程知识总结(一)

这一段开发中一直在处理iOS多线程的问题,但是感觉知识太散了,所以就把iOS中多线程的知识点总结了一下. 1.基本概念 1)什么是进程?进程的特性是什么? 进程是指在系统中正在运行的一个应用程序.    特性: 每个进程之间都是独立的,每个进程都运行在其专用而且受保护的内存空间内. 2)什么是线程?线程和进程的关系是什么? 一个进程要想执行任务,必须要有线程(每一个进程至少要有一个线程),线程是进程的基本单元,一个进程中的所有任务都是在线程中执行 关系: 进程包含线程 3)什么叫多线程? 一个进

iOS/MacOS多线程编程GCD

GCD和Block一起,使得iOS多线程编程变得简单优雅许多.如此优雅简单的多线程API真希望C和C++标准中也会有 One of the technologies for starting tasks asynchronously is Grand Central Dispatch (GCD). This technology takes the thread management code you would normally write in your own applications a

iOS有三种多线程编程的技术

1.NSThread 2.NSOperationQueue 3.GCD Thread 是这三种范式里面相对轻量级的,但也是使用起来最负责的,你需要自己管理thread的生命周期,线程之间的同步.线程共享同一应用程序的部分内存空间, 它们拥有对数据相同的访问权限.你得协调多个线程对同一数据的访问,一般做法是在访问之前加锁,这会导致一定的性能开销.在 iOS 中我们可以使用多种形式的 thread: Cocoa threads: 使用NSThread 或直接从 NSObject 的类方法 perfo

IOS开发 多线程编程 - NSThread

每个iOS应用程序都有个专门用来更新显示UI界面.处理用户的触摸事件的主线程,因此不能将其他太耗时的操作放在主线程中执行,不然会造成主线程堵塞(出现卡机现象),带来极坏的用户体验.一般的解决方案就是将那些耗时的操作放到另外一个线程中去执行,多线程编程是防止主线程堵塞,增加运行效率的最佳方法 iOS支持多个层次的多线程编程,层次越高的抽象程度越高,使用也越方便,也是苹果最推荐使用的方法.下面根据抽象层次从低到高依次列出iOS所支持的多线程编程方法: 1.Thread :是三种方法里面相对轻量级的,