NSThread与GCD

上一篇写到了相册功能的实现,现在问题基本上也都解决了,对于一个程序员的必经之路就是什么都尝试了一遍才能知道最好用的方法,接下来回归正题,这篇文章准备对iOS的多线程开发做一个总结。

进程与线程

进程:指在系统中正在运行的一个应用程序(每个进程都是独立的,都运行在自己专用并且受到保护的内存空间之内)
例如同时在运行的QQ和微信就属于两个独立的进程
线程:线程是进程的基本单元,一个进程的所有任务都在线程中执行
(一个进程想要执行任务,必须要有线程每个进程至少有一条线程)
例如迅雷下载文件就是在线程中执行

iOS多线程

iOS的每个进程启动之后都会建立一个主线程,一般所有的UI任务都放在主线程中执行,现在也可以使用其他线程来进行UI的更新,所以接下来介绍一下iOS多线程的操作。iOS现在常用的多线程开发有一下三个方式:

  1. NSThread

  2. GCD

  3. NSOperation

NSThread

NSThread是一个轻量级的多线程开发,但是需要自行管理生命周期

NSThread的使用

NSThread的创建:

//实例方法创建
-(id)initWithTarget:(id)target selector:@selector(doSomeThing:) object:(id)argument
//类方法创建
+(void)detachNewThreadSelector:@selector(doSomeThing:) toTarget:(id)target withObject:(id)argument
//selector:线程执行的方法,这个selector只能有一个参数,而且不能有返回值
//target: selector消息发送的对象
//argument:传输给target的唯一参数,也可以是nil
//隐式创建
[self performSelectorInBackground:@selector(doSomeThing:) withObject:nil];  

获取当前线程:

NSThread *current = [NSThread currentThread];  

获取主线程:

NSThread *main = [NSThread mainThread];  

暂停当前线程:

// 暂停2s  
//方法一:
[NSThread sleepForTimeInterval:2];  
//方法二:
NSDate *date = [NSDate dateWithTimeInterval:2 sinceDate:[NSDate date]];  
[NSThread sleepUntilDate:date]; 

线程之间的通信:
在指定线程上执行操作:

[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];  

在主线程上执行操作:

[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];  

在当前线程上执行操作:

[self performSelector:@selector(run) withObject:nil];  

以上是NSThread的主要用法,它一定的优缺点,优点就是它比较轻量级,可以直观的控制线程对象。但是缺点也很明显,就是它需要自己来管理线程生命周期,线程同步对数据的加锁操作会有一定的系统资源开销。
NSThread暂时介绍到这几种简单的应用方法,
因为会有涉及到RunLoop的概念,会在后续进行更加详细的分析,RunLoop可以先看一下这篇,讲的比较详细:RunLoop
接下来主要介绍的是经常会用到的也很重要的:

GCD

首先GCD的全称是Grand Central Dispatch,是Apple的libdispatch库的一个名称,使用GCD可以达到很多你所需要的结果,因为GCD本身是苹果公司为多核的并行运算提出的解决方案,它是基于C语言的,完全由系统来管理线程,我们不需要去编写线程代码,只用定义想执行的任务,而且能够提供给我们更大的控制力和大量的底层函数,可以极为简单的在不同代码之间传递上下文,减少上下文的切换,提高系统资源利用效率,减轻系统的负载。
在说GCD之前还有一些东西是要搞清楚的:

任务:

任务就是你需要做的事情,在GCD中一般就是一个block。
任务有两种执行方式:同步执行异步执行
同步执行就是每次只有一个任务被执行,异步执行就是在同一时间可以有多个任务被执行。同步执行操作会阻塞当前的线程,它需要等待当前block中的任务执行完毕之后,当前线程才会继续向下运行。异步执行不会阻塞当前线程,它会直接向下执行。

队列:

队列使用于存放任务的,有三种队列:串行队列并行队列以及Main dispatch queue

串行队列:

串行队列是把任务按FIFO(先进先出)的顺序来执行,也就是说GCD会将任务按先进先出的顺序来取出一个,执行一个然后再执行下一个,一个一个的执行。

并行队列:

并行队列虽然可以同时执行多个任务,但其本质还是按照FIFO的顺序来执行,但是它取出一个任务会放到一个新的线程中执行,它会根据系统负载来选择并发执行这些任务,也就是说如果任务比较多,系统是不会同时执行所有任务的。

Main dispatch queue:

Main dispatch queue与主线程的功能是相同的,其实相当于一个串行队列,可以在主线程中进行UI的更新。

创建队列:

主队列

//主要用于刷新UI 
dispatch_queue_t mainQueue = dispatch_get_main_queue();

获取一个全局队列:

    dispatch_queue_t globalQueue = dispatch_get_global_queue(<#long identifier#>, <#unsigned long flags#>)
#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

long identifier:对应以上四个值,指该队列的优先级,从上到下依次为高优先级,默认优先级,低优先级以及后台优先级,最后一个是后台级别的队列,它会等待所有比它优先级高的队列中的任务完成后或者CPU在空闲的时候才会执行它的任务
unsigned long flags:苹果官方文档是这样解释的: Flags that are reserved for future use。标记是为了未来使用保留的!所以这个参数应该永远指定为0

创建串行队列或并行队列:

//创建串行队列:
dispatch_queue_t serieQueue1 = dispatch_queue_create("mySerieQueue", DISPATCH_QUEUE_SERIAL);
dispatch_queue_t serieQueue2 = dispatch_queue_create("mySerieQueue", DISPATCH_QUEUE_SERIAL);
//创建并行队列
dispatch_queue_t conCurrentQueue = dispatch_queue_create("myConCurrentQueue", DISPATCH_QUEUE_CONCURRENT);

参数一:标识符,用于debug的时候标识唯一的队列,可以为空
参数二:表示创建的队列是串行的还是并行的,DISPATCH_QUEUE_SERIAL与NULL表示串行的,DISPATCH_QUEUE_CONCURRENT表示并行的。

添加任务到队列中:

同步任务:

    dispatch_sync(serieQueue1, ^{
        NSLog(@"我是任务1");
    });
    
    dispatch_sync(serieQueue1, ^{
        NSLog(@"我是任务2");
    });
    
    dispatch_sync(serieQueue1, ^{
        NSLog(@"我是任务3");
    });

异步任务:

dispatch_async(conCurrentQueue, ^{
        NSLog(@"我是任务1");
    });
    
    dispatch_async(conCurrentQueue, ^{
        NSLog(@"我是任务2");
    });
    
    dispatch_async(conCurrentQueue, ^{
        NSLog(@"我是任务3");
    });

在这里可以尝试原本上述的四种情况,打印结果如下:
添加同步任务至串行队列:


添加同步任务至串行队列.png

添加同步任务至并行队列:


添加同步任务至并行队列.png

共计运行三次,三次任务都是顺序执行

添加异步任务至串行队列:


添加异步任务至串行队列.png

共计运行三次,三次任务都是顺序执行

添加异步任务至并行队列:

添加异步任务至并行队列.png

共计运行三次,三次任务不一定按照顺序执行,谁先好先执行谁

队列组:

队列组可以将很多队列放到一个组里,当组里的任务执行完毕后,会通过一个方法通知我们。

//创建队列组
    dispatch_group_t myGroup = dispatch_group_create();
    
    //创建队列
    dispatch_queue_t myGlobalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //添加任务至队列组中,多个任务使用异步方法
    //执行6次循环
    dispatch_group_async(myGroup, myGlobalQueue, ^{
        for (int i = 0; i < 6; i++) {
            NSLog(@"group - 01 - %d",i);
        }
    });
    
    //主队列执行10次循环
    
    dispatch_group_async(myGroup, dispatch_get_main_queue(), ^{
        for (int i = 0; i < 10; i++) {
            NSLog(@"main - 02 - %d",i);
        }
    });
    
    dispatch_group_async(myGroup, myGlobalQueue, ^{
        for (int i = 0; i < 20; i++) {
            NSLog(@"group - 03 - %d",i);
        }
    });
    
    //执行完毕后通知
    dispatch_group_notify(myGroup, dispatch_get_main_queue(), ^{
        NSLog(@"完成 - %@",[NSThread currentThread]);
    });

打印结果:

group

这个功能比较实用,可以把很多任务放在一个组里,并在任务结束后发送通知。
上述就是GCD的常用的方法,过后会有更加详细的讲解,包括死锁的问题。
接下来就是NSOperation和NSOperationQueue了,我今天在实践的时候发现这个东西有很大的可操作性,简单来说就是,同一个任务,使用GCD的话,代码量简洁,并且可读性很高,但是缺点就是无法控制这个任务的进度,也就是说你无法控制线程的取消等,但是使用NSOperation是可以的,可操作性很强,所以暂时不在这里进行讲解,下一篇将会对NSOperation和NSOperationQueue进行详细的分享。

总结

本篇文档只是做了NSThread与GCD的简单使用的分享,具体的深入研究将会在后续更新进行专题分享,如果有写的不对或者不好的地方欢迎大家在评论区留言,我会进行相对应的修改,同时也希望有这方面经验的大神可以互相交流一下,大家不打赏的话点一下喜欢也是可以的,我会继续更新。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容