GCD使用与多线程

项目里有很多需要用到线程的地方,看了一部分GCD的资料,东一块西一块的,好像明白了,但是用的时候也记不起来,所以自己整理一下,mark一下~

1. dispatch object回收机制

dispatch object是GCD对象,和cocoa对象一样是引用计数的,但它不参与垃圾回收机制,iOS6以下无论是否开启ARC都需要手动释放,iOS6及以上版本开启ARC无需手动释放,否则需要手动释放。

2. 队列类型

串行指操作一个接一个执行,并行指操作一起同时执行,可参考串联电路、并联电路理解。

  • dispatch_get_main_queue() :压入主线程中的队列,串行队列,涉及到UI的操作都需要在主线程执行;
  • dispatch_get_global_queue() :全局队列,并行队列,队列有四种优先级:低(DISPATCH_QUEUE_PRIORITY_LOW)、中(DISPATCH_QUEUE_PRIORITY_DEFAULT)、高(DISPATCH_QUEUE_PRIORITY_HIGH)以及iOS 5之后出现的 DISPATCH_QUEUE_PRIORITY_BACKGROUND,根据第一个参数设置,第二个还不支持(设为0或null即可);
  • dispatch_queue_create() : 自己创建的队列,串行队列或并行队列,第一个参数用于测试,第二个设置为DISPATCH_QUEUE_SERIAL(或NULL)创建一个串行队列,DISPATCH_QUEUE_CONCURRENT创建一个并行队列,iOS4.3之前只能设置NULL。
3. 同步线程和异步线程
向系统提交请求,然后不等待系统处理请求时继续处理自己的事情叫做异步,等待系统处理请求完成才继续叫做同步。举个例子,两个小朋友Kimi和Cindy,Kimi需要找到蔬菜,Cindy需要找到水果,如果分头行动,Kimi去自己去找蔬菜,Cindy也自己去找水果,这叫异步;如果Kimi和Cindy一起,一家一家去找,Kimi找蔬菜时,Cindy在旁边等他,Cindy找水果时,Kimi在旁边等她,这叫同步,永远只有一个线程在执行。
GCD里,同步线程是dispatch_sync( queue, block); 异步线程是dispatch_async( queue, block); 具体执行顺序如下:
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        /* 一些操作 1 */
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
             /* 一些操作 2 */
        });
        /* 一些操作 3 */
});
异步线程,先执行1,然后执行3,至于2什么时候执行不清楚,如果2很快速,有可能是123,如果2比较费时,也可能是132;两个线程异步执行,可同时执行;异步线程主要用来执行一些线程安全且比较费时的操作。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
        /* 一些操作 1 */
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
             /* 一些操作 2  */
        });
        /* 一些操作 3 */
});
同步线程,先执行1,然后顺序执行2,没执行完2之前,外面的全局队列会一直等待,等到2执行完之后,才会继续执行3,所以顺序是123。
4. group
dispatch_group_t,group,顾名思义,会创建一个group,这个group可以作为一个整体来监听里面全部block是否完成。以考试为例,一个考场中的学生各自交卷子这件事是异步的,任何一个学生交卷都和其他人没有任何关系,但是这个考场最后给教务处交卷子必须等到所有考生全部交卷收齐全部考卷之后才可以,否则就出教学事故啦,可以利用dispatch_group_t来完成。
    /*1*/
    dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    for(Student obj in class)
        dispatch_group_async(group, queue, ^{
            [obj handInPaper];
        });
    /*2*/
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    /*3*/
    [self handInAllPapers];
    /*4*/
也可以
    /*1*/
    dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    for(Student obj in class)
        dispatch_group_async(group, queue, ^{
            [obj handInPaper];
        });
    /*2*/
    dispatch_group_notify(group, queue, ^{
        /*3*/
        [self handInAllPapers];
    });
    /*4*/
两段代码的区别在于,将全部试卷上交教务处这一操作,在前一段是同步执行,所以是1234的执行顺序;在后一段是异步执行,所以34执行顺序不一定,可能是1234,也可能是1243。
5. dispatch_apply并发迭代
dispatch_apply和block循环体一起,可以完成类似的上述功能,如求一个数组的各个元素的和,
	dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);  
	__block int sum = 0;  
	__block NSArray *array = @[1,2,3,4,5...100];  
	dispatch_apply(100, queue, ^(size_t i) {  
		sum += array[i];  
	}); 
上述代码中,求和是异步进行的,也就是并不是1+2+3+4……,可能是1+100+2+5+8……也可能是56+28+98……,都可能。
6.挂起和激活
激活用dispatch_resume(dispatch_object_t object);挂起用dispatch_suspend(dispatch_object_t object);但是已经运行的block不会停止。挂起期间所有的信号会被积累,合并为单一信号,一旦激活,统一递送。释放object前必须将其resume。
7. dispatch source
这部分比较复杂,单独写一个吧。dispatch source
8.semaphore
信号量,很好理解,顾名思义,是一个信号,有通知和等待两种操作,举个例子就是红绿灯,你要过马路,可是现在是红灯,不行,你被阻塞在马路这头,几十秒以后,绿灯亮了,好了,你可以过去了。这个过程就是个控制台发出绿灯信号,你等待绿灯信号的过程。但是不同于单纯等绿灯信号的是,绿灯信号你不能保存,不用过期作废。可是semaphore是可以计数的,等待到一个semaphore,计数加1,使用掉一个,计数减1,也可以在初始化创建信号量时,创建一定数目的信号量。比如,看视频这件事情,一部电视剧,一天网站更新一集,你跟着看一集,网站更新是一个信号,更新了,信号+1,然后你可以去看,看了一集,信号就-1,如果现在信号计数是0,那么你必须等待下一次更新了,你才能再看一集,但是如果你有一天加班,太累了,虽然网站更新了,但是你没看,那么这个信号+1,不会-1,那么你第二天就可以看两集,当然只是可以看两集,不一定非得看两集;如果这个电视更新到10集,你才发现,那么这个信号量被创建时,就拥有10个计数,当然这十集,你怎么看,是一天看完还是一天一集、一天两集,你说了算,只不过看一集,信号量减1而已,为0了,就需要等待到它大于0才能看,当然之后网站每天更新,信号量计数还是会+1的。我觉得这玩意儿只要是个逻辑问题,自己设计清楚了,使用很简单,一句话的事,不过不要两个线程互相等待,锁死了,就悲剧了。
  • dispatch_semaphore_create(long value) 用来创建信号量,value就是初始计数
  • dispatch_semaphore_signal 发出信号,也就是计数会+1
  • dispatch_semaphore_wait  等待信号,如果计数为0,就不执行,直到等到计数大于0;大于0就继续执行,且计数-1
9.dispatch_set_finalizer_f 释放dispatch对象时的操作
调用dispatch_set_finalizer_f(dispatch_object_t object, dispatch_function_t finalizer); 意思是在object对象被释放时,finalizer函数会被调用。finalizer形式函数如下:
void finalizer(void *context)
{
	/* some action */
}
context利用dispatch_set_context设置:
void *context = ...;
dispatch_set_context(dispatchObject, context);
10.dispatch_once 单例
单例就是该类实例化被限制,只能实例化一个对象。dispatch_once只会被运行一次,而且线程安全(函数函数库多线程环境中被调用时,能够正确地处理各个线程局部变量,使程序功能正确完成)。如果被多个线程调用,会同步至代码块完成。代码如下:
+(Class *)sharedInstance
{
    static Class *sharedInstance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[Class alloc] init];
    });
    return sharedInstance;
}
11.dispatch_set_target_queue
void dispatch_set_target_queue(dispatch_object_t object,dispatch_queue_t queue);
两个参数,第一个要被设置的object,第二个是参照或目标queue。
我的理解,这个函数的作用有三:
  • 第二个参数是全局队列 -- 修改利用dispatch_queue_create创建的queue的优先级,dispatch_queue_create函数是没有优先级这个参数的,默认是DEFAULT,可以利用dispatch_set_target_queue函数来修改,比如queue是HIGH级别的,那么调用dispatch_queue_create,object的优先级也会变成HIGH。
  • 第二个参数是全局队列/main queue -- 设置create创建的queue的目标队列。create创建的queue不会执行block,它会把block传递到它的目标队列然后执行,这个目标队列一般是默认优先级的全局队列,你可以创建一个新的其他优先级的全局队列,也可以获取main queue,传入第二个参数,那create创建的这个queue就会被传递到你新设置的这个queue里执行,其实第一种情况和上面修改默认优先级是一样的嘛。
  • 第二个参数是dispatch_queue_create创建的queue -- 将多个queue串行执行。举个例子,幼儿园给小朋友们接种疫苗,一共三个护士,每个人create一个串行的queue(nurseQueue1、nurseQueue2、nurseQueue3),每个小朋友自己也会create一个queue(childQueue_1……childQueue_N),将每个childQueue里的block分别传递至nurseQueue,那么,每个护士的队列中的小朋友都会被串行接种疫苗,这样做的好处是一个nurseQueue被挂起或恢复时,整个队列组都会被挂起或恢复,而且一个小朋友只会由一名护士接种疫苗。
12.dispatch_after延迟
dispatch_after用于在一定时间的延迟后,将block提交至queue,但是只是提交至,不是执行。代码如下:
	//执行时间,第一个表示现在,第二个表示过timeout秒后,NSEC_PER_SEC是一个纳秒转秒的常数
	dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, timeout * NSEC_PER_SEC);
	dispatch_after(time, dispatch_get_main_queue(), ^{
		NSLog(@"work!");//在time时间,也就是现在开始timeout后,将这个block递送至主线程
	});

自我理解,如有错误,还望指正,谢谢!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值