IOS多线程编程的3种实现方法

2016-02-19 10:40 5 1 收藏

有一种朋友不在生活里,却在生命力;有一种陪伴不在身边,却在心间。图老师即在大家的生活中又在身边。这么贴心的服务你感受到了吗?话不多说下面就和大家分享IOS多线程编程的3种实现方法吧。

【 tulaoshi.com - 编程语言 】

前言

在多线程简介中,我已经说明过了,为了提高界面的流畅度以及用户体验。我们务必要把耗时的操作放到别的线程中去执行,千万不要阻塞主线程。
iOS中有以下3种多线程编程方法:

NSThreadGrand Centeral Dispatch(GCD)NSOperation和NSOperationQueue

1.NSThread

这是最轻量级的多线程的方法,使用起来最直观的多线程编程方法。但是因为需要自己管理线程的生命周期,线程同步。经常使用NSThread进行调试,在实际项目中不推荐使用。

//获取当前线程NSThread *current = [NSThread currentThread];//获取主线程NSThread *main = [NSThread mainThread];NSLog(@"当前线程 --- %@",current);NSLog(@"主线程 --- %@",main);

控制台输出结果:

2015-11-22 22:30:29.572 多线程demo[1289:2925847] 当前线程 --- NSThread: 0x7fc0e1401dc0{number = 1, name = main}
2015-11-22 22:30:29.572 多线程demo[1289:2925847] 主线程 --- NSThread: 0x7fc0e1401dc0{number = 1, name = main}
从结果我们看出当前的线程就是主线程,number相当于线程的id,name是线程的名称,主线程的number就是1

阻塞线程:

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

//阻塞线程3秒
[NSThread sleepForTimeInterval:3];
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];

2.GCD(Grand Central Dispatch)

GCD是基于C语言底层API实现的一套多线程并发机制,非常的灵活方便,在实际的开发中使用很广泛。
简单来说CGD就是把操作放在队列中去执行。
你只需定义好操作和队列就可以了,不需要直接控制线程的创建和销毁,线程的生命周期由队列来管理。

队列:负责操作的调度和执行,有先进先出(FIFO)的特点。也就是说先加入队列的操作先执行,后加入的后执行。

队列有两种:

串行队列:

队列中的操作只会按顺序执行,你可以想象成单窗口排队。


并行队列:

队列中的操作可能会并发执行,这取决与操作的类型,你可以想象成多窗口排队。

代码如下:
//创建串行队列
dispatch_queue_t q = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
//创建并行队列
dispatch_queue_t q = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
my_serial_queue和my_concurrent_queue是队列的名字标签,为了与其他的队列区分,在一个项目里面必须是唯一的。
DISPATCH_QUEUE_SERIAL表示串行队列
DISPATCH_QUEUE_CONCURRENT表示并行队列

操作同样也分两种类型:
同步操作:只会按顺序执行,执行顺序是确定的。
异步操作:在串行队列中执行顺序确定,在并行队列中执行顺序不确定

使用block来定义操作要执行的代码,q是已经定义好的,操作要加入的队列

//定义同步操作dispatch_sync(q, ^{  //要执行的代码  });//定义异步操作dispatch_async(q, ^{  //要执行的代码   });

下面我们看一下同步,异步操作加入到串行和并行队列里面,执行的顺序和特点:
1.同步操作不管加入到何种队列,只会在主线程按顺序执行

dispatch_queue_t q_serial = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);dispatch_queue_t q_concurrent = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);for (int i = 0; i  5; ++i) {  dispatch_sync(q_serial, ^{NSLog(@"串行队列里的同步任务 %@ %d", [NSThread currentThread], i);  });}for (int i = 0; i  5; ++i) {  dispatch_sync(q_concurrent, ^{NSLog(@"并行队列里的同步任务 %@ %d", [NSThread currentThread], i);  });}

下面是控制台输出结果:

2015-11-23 00:40:36.862 01.GCD演练[1952:3613752] 串行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 0
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 1
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 2
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 3
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 串行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 4
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 并行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 0
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 并行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 1
2015-11-23 00:40:36.863 01.GCD演练[1952:3613752] 并行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 2
2015-11-23 00:40:36.864 01.GCD演练[1952:3613752] 并行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 3
2015-11-23 00:40:36.864 01.GCD演练[1952:3613752] 并行队列里的同步任务 NSThread: 0x7ff833505450{number = 1, name = main} 4

2.异步操作只在非主线程的线程执行,在串行队列中异步操作会在新建的线程中按顺序执行。

代码如下:
    dispatch_queue_t q_serial = dispatch_queue_create("my_serial_queue", DISPATCH_QUEUE_SERIAL);
    for(int i = 0; i 5; ++i){
        dispatch_async(q_serial, ^{
            NSLog(@"串行队列 -- 异步任务 %@ %d", [NSThread currentThread], i);
        });
    }

因为是异步操作,所以会新建一个线程。又因为加入到串行队列中,所以所有的操作只会按顺序执行。

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

2015-11-23 01:03:22.372 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 NSThread: 0x7fb593d42f50{number = 2, name = (null)} 0
2015-11-23 01:03:23.373 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 NSThread: 0x7fb593d42f50{number = 2, name = (null)} 1
2015-11-23 01:03:24.374 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 NSThread: 0x7fb593d42f50{number = 2, name = (null)} 2
2015-11-23 01:03:25.375 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 NSThread: 0x7fb593d42f50{number = 2, name = (null)} 3
2015-11-23 01:03:26.376 01.GCD演练[2081:3627139] 串行队列 -- 异步任务 NSThread: 0x7fb593d42f50{number = 2, name = (null)} 4

3.异步操作,并行队列

代码如下:
    dispatch_queue_t q_concurrent = dispatch_queue_create("my_concurrent_queue", DISPATCH_QUEUE_CONCURRENT);
    for(int i = 0; i 5; ++i){
        dispatch_async(q_concurrent, ^{
            NSLog(@"并行队列 -- 异步任务 %@ %d", [NSThread currentThread], i);
        });
    }

理论上并行队列会给每一个异步操作新建线程,然后让所有的任务并发执行。但是实际上系统能创建的线程数量是有限的,当创建的线程达到最大线程数以后,后面的异步操作就需要等待前面的操作执行完毕才能得到执行。哪个线程操作执行完毕,就把等待的异步任务安排到哪个线程。直到所有的操作执行完毕。
你可以把上述代码的循环次数改成5000就可以观察到此现象。
2015-11-23 01:14:15.282 01.GCD演练[2165:3634728] 并行队列 -- 异步任务 NSThread: 0x7fb3b841b0a0{number = 4, name = (null)} 3
2015-11-23 01:14:15.282 01.GCD演练[2165:3634724] 并行队列 -- 异步任务 NSThread: 0x7fb3b8514da0{number = 3, name = (null)} 0
2015-11-23 01:14:15.282 01.GCD演练[2165:3634726] 并行队列 -- 异步任务 NSThread: 0x7fb3b8604db0{number = 5, name = (null)} 2
2015-11-23 01:14:15.282 01.GCD演练[2165:3634725] 并行队列 -- 异步任务 NSThread: 0x7fb3b86119d0{number = 2, name = (null)} 1
2015-11-23 01:14:15.285 01.GCD演练[2165:3634729] 并行队列 -- 异步任务 NSThread: 0x7fb3b87011f0{number = 6, name = (null)} 4

3.NSOperation & NSOperationQueue

虽然GCD的功能已经很强大了,但是它使用的API依然是C语言的。在某些时候,在面向对象的objective-c中使用起来非常的不方便和不安全。

所以苹果公司把GCD中的操作抽象成NSOperation对象,把队列抽象成NSOperationQueue对象。


抽象为NSOperation & NSOperationQueue以后的好处有一下几点:

代码风格统一了,我们不用在面向对象的objective-C中写面对过程的C语言代码了。
我们知道在GCD中操作的执行代码都是写在匿名的block里面,那么我们很难做到给操作设置依赖关系以及取消操作。这些功能都已经封装到NSOperation对象里面了。^-^
NSOperationQueue对象比GCD中队列更加的强大和灵活,比如:设置并发操作数量,取消队列中所有操作。
NSOperation分为NSInvocationOperation和NSBlockOperation

NSInvocationOperation的使用

//首先定义一个NSOperationQueue对象NSOperationQueue *queue = [[NSOperationQueue alloc] init];NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"这里可以穿参数"];[queue addOperation:op];//把操作加入队列中即开始执行- (void)operationAction:(id)obj{  NSLog(@"%@ - obj : %@", [NSThread currentThread], obj);}

输出为:

2015-11-23 02:55:19.067 多线程demo[2604:3686934] NSThread: 0x7f9dfa443510{number = 2, name = (null)} - obj : 这里可以穿参数
NSBlockOperation的使用

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
    [self operationAction:@"这是NSBlockOperation"];
}];
[queue addOperation:op];
输出为:

2015-11-23 02:56:11.812 多线程demo[2617:3687872] NSThread: 0x7fa983f10a50{number = 2, name = (null)} - obj : 这是NSBlockOperation
设置依赖关系(执行顺序)

    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"op1"];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operationAction:) object:@"op2"];
    //op2在op1之后执行
    [op2 addDependency:op1];//这里需要注意,一定要在addOperation之前设置依赖关系
   
    [queue addOperation:op1];
    [queue addOperation:op2];
输出为:

2015-11-23 02:57:40.283 多线程demo[2661:3689737] NSThread: 0x7fb663e132d0{number = 2, name = (null)} - obj : op1
2015-11-23 02:57:40.284 多线程demo[2661:3689737] NSThread: 0x7fb663e132d0{number = 2, name = (null)} - obj : op2
没有设置依赖关系的输出:

2015-11-23 03:00:45.939 多线程demo[2709:3692307] NSThread: 0x7fe951d0d8a0{number = 2, name = (null)} - obj : op2
2015-11-23 03:00:45.939 多线程demo[2709:3692308] NSThread: 0x7fe951c24720{number = 3, name = (null)} - obj : op1
到这里你应该发现了,在NSOperation & NSOperationQueue中,我们不需要再像GCD那样定义操作的类型和队列的类型和控制操作的执行顺序了,你只需要直接设定操作的执行顺序就可以了。

来源:http://www.tulaoshi.com/n/20160219/1595429.html

延伸阅读
       在 Java 程序中使用多线程要比在 C 或 C++ 中容易得多,这是因为 Java 编程语言提供了语言级的支持。本文通过简单的编程示例来说明 Java 程序中的多线程是多么直观。读完本文以后,用户应该能够编写简单的多线程程序。 为什么会排队等待? 下面的这个简单的 Java 程序完成四项不相关的任务。这...
多线程是许多操作系统所具有的特性,它能大大提高程序的运行效率,所以多线程编程技术为编程者广泛关注。目前微软的.Net战略正进一步推进,各种相关的技术正为广大编程者所接受,同样在.Net中多线程编程技术具有相当重要的地位。本文我就向大家介绍在.Net下进行多线程编程的基本方法和步骤。    开始新线程    ...
Java5增加了新的类库并发集java.util.concurrent,该类库为并发程序提供了丰富的API多线程编程在Java 5中更加容易,灵活。本文通过一个网络服务器模型,来实践Java5的多线程编程,该模型中使用了Java5中的线程池,阻塞队列,可重入锁等,还实践了Callable, Future等接口,并使用了Java 5的另外一个新特性泛型。 简介 本文将实现...
       什么是线程? 每个正在系统上运行的程序都是一个进程。每个进程包含一到多个线程。进程也可能是整个程序或者是部分程序的动态执行。线程是一组指令的集合,或者是程序的特殊段,它可以在程序里独立执行。也可以把它理解为代码运行的上下文。所以线程基本上是轻量级的进程,它负责在单个程序里执行...
标签: Web开发
  我平时比较喜欢从网上听歌,有些链接下载速度太慢了。如果用HttpURLConnection类的方法打开连接,然后用InputStream类获得输入流,再用BufferedInputStream构造出带缓冲区的输入流,如果网速太慢的话,无论缓冲区设置多大,听起来都是断断续续的,达不到真正缓冲的目的。于是尝试编写代码实现用缓冲方式读取远程文件,以下贴出的代码...

经验教程

673

收藏

16
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部