iOS 开发--多线程

前面在《Bison眼中的iOS开发多线程是这样的(二)》一文中讲完了多线程的NSThread,不难发现这种方式的多线程实现起来非常的复杂,为了简化多线程的开发,iOS提供了GCD来实现多线程。GCD有俩个核心的概念:

队列:队列负责管理开发者提交的任务,GCD队列始终以先进先出的方式来处理任务,但由于任务的执行时间并不相同,因此先处理的任务并不一定先结束。队列既可是串行队列,也可是并发队列,串行队列每次只处理一个任务,必须前一任务完成后,才会执行下一任务;并放队列则可同时处理多个任务,So将会有多个任务并发执行。队列底层会维护一个线程池来处理用户提交的任务,线程池的作用就是执行队列管理的任务。串行队列底层的线程池只要维护一个线程即可,并发队列则想反。

任务:任务则为用户提交给队列的工作单元,这些任务将会提交给队列底层维护的线程池执行,因此这些任务会以多线程的方式执行。

综上所述,不难发现,使用GCD只需俩步即可。

1.创建队列。
2.将任务提交给队列。

接下来我让我们详细的玩一玩这GCD把??

首先咱先创建队列

//获取当前执行代码所在的队列(已废弃)
dispatch_get_current_queue(void);

/*根据制定优先级、额外的旗标来获取系统的全局并发队列,第一个参数指定dispatch queue的优先级,取值可以是DISPATCH_QUEUE_PRIORITY_HIGHT、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW或DISPATCH_QUEUE_PRIORITY_BACKGROUND(iOS 5.0加入的); 第二个参数,目前只能为0或NULL
*/
dispatch_get_global_queue(long identifier, unsigned long flags);

//获取主线程相关联的串行队列
dispatch_get_main_queue(void)

/*
    根据指定字符串标签创建队列。第二个参数可控制创建串行队列还是并发队列,如果将第二个参数设置为“DISPATCH_QUEUE_SERIAL”,则为串行
    设为“DISPATCH_QUEUE_CONCURRENT”为并发队列。在没有启动ARC机制的情况下,通常这种方式创建的队列需要调用dispatch_release()释放引用计数
*/
dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);

//获取指定队列的字符串标签,dispatch_queue_t代表一个队列
dispatch_queue_get_label(dispatch_queue_t queue);

根据上面的方法,可以创建如下几种队列

1.获取系统默认的全局并发队列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

2.获取系统主线程关联的串行队列

dispatch_queue_t queue = dispatch_get_main_queue();

3.创建串行队列

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

4.创建并发队列

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

异步提交任务

iOS提供了如下函数来向队列提交任务。下面这些函数很多都有俩个版本:一个接收代码块作为参数的版本,一个接收函数作为参数的版本;其中接收函数的函数名最多多了_f后缀,而且会多一个参数,用于向函数传入应用程序定义的上下文。

//将代码块以异步方式提交给指定队列,该队列底层的线程池将负责执行该代码块
dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

//将函数以异步方式提交给指定队列,该队列底层的线程池将负责执行该函数
dispatch_async_f(dispatch_queue_t queue,void *context,dispatch_function_t work);

//将代码块以同步方式提交给指定队列,该队列底层的线程池将负责执行该代码块
dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

//将函数以同步方式提交给指定队列,该队列底层的线程池将负责执行该函数
dispatch_sync_f(dispatch_queue_t queue,void *context,dispatch_function_t work);

//将代码块以异步方式提交给指定的队列,该队列底层的线程池将负责在when指定的时间点执行该代码块
dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block);

//将函数以异步方式提交给指定的队列,该队列底层的线程池将负责在when指定的时间点执行该函数
dispatch_after_f(dispatch_time_t when,dispatch_queue_t queue,void *context,dispatch_function_t work);

//将代码块以异步方式提交给指定的队列,该队列底层的线程池将多次重复执行该代码块
dispatch_apply(size_t iterations, dispatch_queue_t queue,void (^block)(size_t));

//将函数以异步方式提交给指定的队列,该队列底层的线程池将多次重复执行该函数
dispatch_apply_f(size_t iterations, dispatch_queue_t queue,void *context,void (*work)(void *, size_t));

//将代码块提交给指定队列,该队列底层的线程池控制在应用的某个生命周期内仅执行一次。predicate 是指向dispatch_once_t变量的指针,判断是否已经执行过,runtime中很常用
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

下面是简单的几个案例

// 定义2个队列
dispatch_queue_t serialQueue;
dispatch_queue_t concurrentQueue;
- (void)viewDidLoad
{
    [super viewDidLoad];
    // 创建串行队列
    serialQueue = dispatch_queue_create("fkjava.queue", DISPATCH_QUEUE_SERIAL);
    // 创建并发队列
    concurrentQueue = dispatch_queue_create("fkjava.queue"
    , DISPATCH_QUEUE_CONCURRENT);
}
- (IBAction)serial:(id)sender
{
    // 依次将2个代码块提交给串行队列
    // 必须等到第1个代码块完成后,才能执行第2个代码块。
    dispatch_async(serialQueue, ^(void)
    {
        for (int i = 0 ; i < 100; i ++)
        {
            NSLog(@"%@===……-……==%d"  , [NSThread currentThread] , i);
        }
    });
    dispatch_async(serialQueue, ^(void)
    {
        for (int i = 0 ; i < 100; i ++)
        {
            NSLog(@"%@------%d" , [NSThread currentThread] , i);
        }
    });
}

- (IBAction)concurrent:(id)sender
{
    // 依次将2个代码块提交给并发队列
    // 两个代码块可以并发执行
    dispatch_async(concurrentQueue, ^(void)
    {
        for (int i = 0 ; i < 100; i ++)
        {
            NSLog(@"%@===……-……==%d"  , [NSThread currentThread] , i);
        }
    });
    dispatch_async(concurrentQueue, ^(void)
    {
        for (int i = 0 ; i < 100; i ++)
        {
            NSLog(@"%@------%d" , [NSThread currentThread] , i);
        }
    });

}

编译执行不难看出,第一个是串行队列,第二个是并行队列。

下面我们将简单的举个异步下载图片的例子

代码如下:

// 将代码块提交给系统的全局并发队列
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0),
^(void){
    NSString* url = @"http://allluckly.cn/images/blog/applepay/6.png";
    // 从网络获取数据
    NSData *data = [[NSData alloc]
    initWithContentsOfURL:[NSURL URLWithString:url]];
    // 将网络数据初始化为UIImage对象
    UIImage *image = [[UIImage alloc]initWithData:data];
    if(image != nil)
    {
        // 将代码块提交给主线程关联的队列,该代码块将会由主线程完成
        dispatch_async(dispatch_get_main_queue(), ^{
            self.iv.image = image;
        }); // ①
    }
    else
    {
        NSLog(@"---下载图片出现错误---");
    }
});

这里值的注意的是我们的图片是http的,xcode7以上必须设置下info.plist文件设置下网络,否则无法成功!
不难看出上面的送上面的例子中通过创建全局并发队列,该代码块负责从网络下载图片,下载完成后交给主线程执行。

同步提交任务

dispatch_async()函数则会以同步方式提交代码块,该函数必须等到代码块执行结束才会返回。如果程序使用该函数先后提交了俩个代码块(并发队列),也必须等第一个任务执行完后才会执行第二个任务。如下??

- (IBAction)clicked:(id)sender
{
    // 以同步方式先后提交2个代码块
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    , ^(void){
        for (int i = 0 ; i < 100; i ++)
        {
            NSLog(@"%@=====%d"  , [NSThread currentThread] , i);
            [NSThread sleepForTimeInterval:0.1];
        }
    });
    // 必须等第一次提交的代码块执行完成后,dispatch_sync()函数才会返回,
    // 程序才会执行到这里,才能提交第二个代码块。
    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    , ^(void){
        for (int i = 0 ; i < 100; i ++)
        {
            NSLog(@"%@-----%d"  , [NSThread currentThread] , i);
            [NSThread sleepForTimeInterval:0.1];
        }
    });
}

多次执行任务

dispatch_apply()函数将代码块多次重复执行,如果该代码块被提交给并发队列,系统可以使用多个线程并发执行同一个代码块。下面我们简单的看个??.

- (IBAction)clicked:(id)sender
{
    // 控制代码块执行5次
    dispatch_apply(5
    , dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    // time形参代表当前正在执行第几次
    , ^(size_t time)
    {
        NSLog(@"===执行【%lu】次===%@" , time, [NSThread currentThread]);
    });
}

只执行一次任务

dispatch_once()将代码块提交给指定队列,该队列底层的线程池控制在应用的某个生命周期内仅执行一次。predicate 是指向dispatch_once_t变量的指针,判断是否已经执行过,runtime中很常用。系统直接用主线程执行该函数提交的代码块。下面我们简单的看个??.

- (IBAction)clicked:(id)sender
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"==执行代码块==");
        // 线程暂停3秒
        [NSThread sleepForTimeInterval:3];
    });
}

文/Bison(简书作者)
原文链接:http://www.jianshu.com/p/2f18ffcd4c22
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

时间: 2024-12-27 04:46:12

iOS 开发--多线程的相关文章

iOS开发多线程篇—线程的状态

iOS开发多线程篇—线程的状态 一.简单介绍 线程的创建: self.thread=[[NSThread alloc]initWithTarget:self selector:@selector(test) object:nil]; 说明:创建线程有多种方式,这里不做过多的介绍. 线程的开启: [self.thread start]; 线程的运行和阻塞: (1)设置线程阻塞1,阻塞2秒 [NSThread sleepForTimeInterval:2.0]; (2)第二种设置线程阻塞2,以当前时

iOS开发多线程之自定义NSOperation

iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 1 // 2 // YYViewController.h 3 // 01-自定义Operation 4 // 5 // Created by apple on 14-6-26. 6 // Copyright (c) 2014年 itcase. All rig

iOS开发多线程篇—自定义NSOperation

iOS开发多线程篇—自定义NSOperation 一.实现一个简单的tableView显示效果 实现效果展示: 代码示例(使用以前在主控制器中进行业务处理的方式) 1.新建一个项目,让控制器继承自UITableViewController. 1 // 2 // YYViewController.h 3 // 01-自定义Operation 4 // 5 // Created by apple on 14-6-26. 6 // Copyright (c) 2014年 itcase. All rig

iOS开发多线程篇 09 —NSOperation简单介绍

iOS开发多线程篇—NSOperation简单介绍 一.NSOperation简介 1.简单说明 NSOperation的作?:配合使用NSOperation和NSOperationQueue也能实现多线程编程 NSOperation和NSOperationQueue实现多线程的具体步骤: (1)先将需要执行的操作封装到一个NSOperation对象中 (2)然后将NSOperation对象添加到NSOperationQueue中 (3)系统会?动将NSOperationQueue中的NSOpe

iOS开发多线程篇 05 —GCD介绍

iOS开发多线程篇—GCD介绍 一.简单介绍 1.什么是GCD? 全称是Grand Central Dispatch,可译为“牛逼的中枢调度器” 纯C语言,提供了非常多强大的函数 2.GCD的优势 GCD是苹果公司为多核的并行运算提出的解决方案 GCD会自动利用更多的CPU内核(比如双核.四核) GCD会自动管理线程的生命周期(创建线程.调度任务.销毁线程) 程序员只需要告诉GCD想要执行什么任务,不需要编写任何线程管理代码 3.提示 (1)GCD存在于libdispatch.dylib这个库中

iOS开发多线程篇—多线程简单介绍

iOS开发多线程篇—多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过“活动监视器”可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行 3.线程

iOS开发多线程篇—多线程的的相关概念(1)

iOS开发多线程篇-多线程简单介绍 一.进程和线程 1.什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内 比如同时打开QQ.Xcode,系统就会分别启动2个进程 通过"活动监视器"可以查看Mac系统中所开启的进程 2.什么是线程 1个进程要想执行任务,必须得有线程(每1个进程至少要有1条线程) 线程是进程的基本执行单元,一个进程(程序)的所有任务都在线程中执行 比如使用酷狗播放音乐.使用迅雷下载电影,都需要在线程中执行

IOS开发——多线程编程

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

iOS开发多线程篇—GCD的基本使用

iOS开发多线程篇—GCD的基本使用 一.主队列介绍 主队列:是和主线程相关联的队列,主队列是GCD自带的一种特殊的串行队列,放在主队列中得任务,都会放到主线程中执行. 提示:如果把任务放到主队列中进行处理,那么不论处理函数是异步的还是同步的都不会开启新的线程. 获取主队列的方式: dispatch_queue_t queue=dispatch_get_main_queue(); (1)使用异步函数执行主队列中得任务,代码示例: 1 // 2 // YYViewController.m 3 //

iOS开发多线程篇 10 —NSOperation基本操作

iOS开发多线程篇—NSOperation基本操作 一.并发数 (1)并发数:同时执?行的任务数.比如,同时开3个线程执行3个任务,并发数就是3 (2)最大并发数:同一时间最多只能执行的任务的个数. (3)最?大并发数的相关?方法 - (NSInteger)maxConcurrentOperationCount;- (void)setMaxConcurrentOperationCount:(NSInteger)cnt; 说明:如果没有设置最大并发数,那么并发的个数是由系统内存和CPU决定的,可能