相关文章链接:

1.多线程简介

2.实现多线程的3种方法

......待续

前言

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

iOS中有以下3种多线程编程方法:

  1. NSThread
  2. Grand Centeral Dispatch(GCD)
  3. 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

阻塞线程:

//阻塞线程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);
});
}

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

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那样定义操作的类型和队列的类型和控制操作的执行顺序了,你只需要直接设定操作的执行顺序就可以了。

iOS开发进阶-实现多线程的3种方法的更多相关文章

  1. iOS开发之动画编程的几种方法

    iOS开发之动画编程的几种方法 IOS中的动画总结来说有五种:UIView<block>,CAAnimation<CABasicAnimation,CATransition,CAKe ...

  2. IOS开发中数据持久化的几种方法--NSUserDefaults

    IOS开发中数据持久化的几种方法--NSUserDefaults IOS 开发中,经常会遇到需要把一些数据保存在本地的情况,那么这个时候我们有以下几种可以选择的方案: 一.使用NSUserDefaul ...

  3. iOS开发进阶--1.多线程简介

    学习是由已知的知识模型推理未知的知识模型的的过程. 本文适合学习完objective-c基础,想进一步提高做iOS开发的同学阅读. 在说线程的时候,我们先看看进程. 1.进程 每一个运行在系统中的应用 ...

  4. 【转】 ios开发之倒计时实现的两种方法

    原文:http://blog.csdn.net/kylinbl/article/details/8972261 方法1:使用NSTimer来实现 主要使用的是NSTimer的scheduledTime ...

  5. iOS开发进阶

    <iOS开发进阶>基本信息作者: 唐巧 出版社:电子工业出版社ISBN:9787121247453上架时间:2014-12-26出版日期:2015 年1月开本:16开页码:268版次:1- ...

  6. iOS开发网络篇—多线程断点下载

    iOS开发网络篇—多线程断点下载 说明:本文介绍多线程断点下载.项目中使用了苹果自带的类,实现了同时开启多条线程下载一个较大的文件.因为实现过程较为复杂,所以下面贴出完整的代码. 实现思路:下载开始, ...

  7. iOS开发 跳转场景的三种方式

    iOS开发 跳转场景的三种方式 2012年10月17日, 15:32 假设A跳转到B,三种方法:1.按住ctrl键,拖动A上的控件(比如说UIButton)到B上,弹出菜单,选择Modal.不需要写任 ...

  8. iOS开发进阶(唐巧)读书笔记(一)

    如何提高iOS开发技能 1.阅读博客:https://github.com/tangqiaoboy/iOSBlogCN 40多位iOS开发博主的博客地址 2.读书:每年阅读一本高质量的iOS开发书籍 ...

  9. (OPC Client .NET 开发类库)网上很多网友都有提过,.NET开发OPC Client不外乎下面三种方法

    1. 背景 OPC Data Access 规范是基于COM/DCOM定义的,因此大多数的OPC DA Server和client都是基于C++开发的,因为C++对COM/DCOM有最好的支持.现在, ...

随机推荐

  1. ASPNETMVC多语言方案

    ASPNETMVC多语言方案 前言: 好多年没写文章了,工作很忙,天天加班, 每天都相信不用多久,就会升职加薪,当上总经理,出任CEO,迎娶白富美,走上人生巅峰,想想还有点小激动~~~~ 直到后来发生 ...

  2. JavaScript高级程序设计53.pdf

    共有的表单字段方法 每个表单字段都有两个方法:focus()和blur(),其中focus()用于将浏览器焦点设置到表单字段,激活表单字段.可以侦听页面的load事件 EventUtil.addHan ...

  3. Bzoj 1616: [Usaco2008 Mar]Cow Travelling游荡的奶牛 动态规划

    1616: [Usaco2008 Mar]Cow Travelling游荡的奶牛 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 1006  Solved: ...

  4. First Adventures in Google Closure -摘自网络

    Contents Introduction Background Hello Closure World Dependency Management Making an AJAX call with ...

  5. php 生成正态分布随机数

    这个函数比较冷门,连官方文档都不太齐全 function stats_rand_gen_normal(av, sd);   //av是平均值,sd是标准偏差 使用这个函数需要,安装pecl stats ...

  6. VirtualBox上Ubuntu 共享文件夹

    1. virtualbox 菜单栏中设备-->共享文件夹,添加一个共享文件夹,比如共享文件夹路径是D:/share,共享文件夹名称是share. 2. 进入虚拟Ubuntu,在命令行终端输入: ...

  7. Android基于XMPP Smack Openfire下学习开发IM(六)总结

    不管学习什么都应该总结 这里我把关于Xmpp的一些方法整理到一个工具类中了 我就分享给大家 XmppConnection.java package com.techrare.utils; import ...

  8. 8-6-Exercise

    HDU 1003    Max Sum 题意:给出一串数字,求出其中某段连续的数字之和最大的值,同时要输出起点的位置和终点的位置~~~ 方法一: 用sum记录某一段和的值,maxx为目前为止最大的su ...

  9. Linux下多任务间通信和同步-信号

    Linux下多任务间通信和同步-信号 嵌入式开发交流群280352802,欢迎加入! 1.概述 信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式.信号可以直接进行用户空间进程和内核进程之间的 ...

  10. Spring学习总结

    spring是当前流行的一个框架.在学习spring之前当然的先准备一些jar包.可以在官网找找,(也可以留言,网盘分享): 基于IDEA环境学习. 第一天完成的任务: 1.使用spring框架输出一 ...