iOS多线程:GCD

异步执行任务的技术之一,

  用我们难以置信的非常简洁的记述方法,实现了极为复杂繁琐的多线程编程。

 

dispatch_async函数追加赋值变了queue的Dispatch Queue中,这样可以在另一线程中执行。

 

存在两种DIspatchQueue,一种事等待现在执行中处理的SerialDisPatch,一种是不等待现在执行中的ConcurrentDispatchQueue

 

SerialDisPatch  串行

ConcurrentDispatchQueue  并发

 

1.第一种方法 同归gcd的api生成

dispatch_queue_create

函数可生成DispatchQucue,

列如: dispatch_queue_t myser = dispatch_queue_create("com", NULL);NULL表示为串行SerialDisPatch

 

dispatch_queue_t myser = dispatch_queue_create("com", DISPATCH_QUEUE_CONCURRENT);

     dispatch_async(myser, ^{

        

     });

DISPATCH_QUEUE_CONCURRENT表示为并发。

 

第二种方法获取系统标准提供的Dispatch Queue

 

Main Dispatch Queue 和Global Dispatch Queue(四个优先级)

Main Dispatch Queue是在主线程种执行的Dispatch Queue。  是串行

Global Dispatch Queue  是并发  

获取方式

 dispatch_queue_t aa = dispatch_get_main_queue();

    dispatch_queue_t high= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

    dispatch_queue_t default1= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_queue_t low= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0);

    dispatch_queue_t background= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

 

Main Dispatch Queue 和Global Dispatch Queue并用

  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

      

       dispatch_async(dispatch_get_main_queue(), ^{

          

       });

   });

 

2.dispatch_set_target_queue  变更优先级

函数生成的Dispatch Queue  不管是串行还是并发都是使用Global Dispatch Queue默认优先级,而变更生成的Dispatch Queue的执行优先级要使用dispatch_set_target_queue函数

 

3.dispatch_after

假如3秒后执行  追加处理

 

4.Dispatch Group   多个处理结束后想执行结束处理。

  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_t group = dispatch_group_create();

    dispatch_group_async(group, queue, ^{

        NSLog(@"1");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"2");

    });

    dispatch_group_async(group, queue, ^{

        NSLog(@"3");

    });

    dispatch_group_notify(group, queue, ^{

        NSLog(@"4");

    });

2014-03-30 01:01:30.394 arcc[2367:202023] 3

2014-03-30 01:01:30.394 arcc[2367:202022] 2

2014-03-30 01:01:30.394 arcc[2367:202021] 1

2014-03-30 01:01:30.396 arcc[2367:202021] 4

结果 前3个没有顺序,因为优先级别一样。dispatch_group_notify追加处理。还可以设置dispatch_group_wait等待,不过要设置等待时间。等待时间可以超时可以固定。

 

5.dispatch_barrier_async

访问数据库和文件时  可以并发的访问,不会发生数据竞争,前面都完成了才会追加执行以后的。

  dispatch_queue_t queue = dispatch_queue_create("com", DISPATCH_QUEUE_CONCURRENT);(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

   dispatch_async(queue, ^{

       NSLog(@"1");

   });

    dispatch_async(queue, ^{

        NSLog(@"2");

    });

   dispatch_barrier_async(queue, ^{

       NSLog(@"3");

   });

    dispatch_async(queue, ^{

        NSLog(@"4");

    });

    dispatch_async(queue, ^{

        NSLog(@"5");

    });

2014-03-30 01:13:11.787 arcc[2431:205805] 2

2014-03-30 01:13:11.787 arcc[2431:205806] 1

2014-03-30 01:13:11.788 arcc[2431:205806] 3

2014-03-30 01:13:11.788 arcc[2431:205806] 4

2014-03-30 01:13:11.789 arcc[2431:205806] 5

 

6.dispatch_sync

同步 ,

这个列子 死锁

 dispatch_queue_t queue = dispatch_get_main_queue();

    dispatch_sync(queue, ^{

        NSLog(@"hello.死锁");

    });

 

7.dispatch_apply

 是dispatch_sync和Dispatch Group关联的api,

dispatch_apply会等待dispatch_apply块中的Dispatch Queue执行完后再执行后面的。

 

8.dispatch_suspend/dispatch_resume

挂起 /恢复

 

9.Dispatch Semaphore

dispatch_semaphore_wait  为0市等待,否则就减1

dispatch_semaphore_signal 信号量+1

进行细粒控制,怕程序异常结束

  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_semaphore_t semaphore =  dispatch_semaphore_create(1);

    NSMutableArray * array = [[NSMutableArray alloc]init];

    for (int i = 0; i< 100000; i++)

    {

        dispatch_async(queue, ^{

            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

            [array addObject:[NSNumber numberWithInt:i]];

            dispatch_semaphore_signal(semaphore);

        });

    }

10.dispatch_once

只执行一次

可以用在单利模式下面

 

    static dispatch_once_t pred;

    dispatch_once(&pred, ^{

        //初始化

    });

11.Dispatch I/O

大文件时,用它多线程读取

 

12、dispatch_block

在向队列中添加任务时,可以直接在对应的函数中添加 block。但是如果想对任务进行操作,比如监听任务、取消任务,就需要获取对应的 block。

 

dispatch_block_flags

 

DISPATCH_ENUM(dispatch_block_flags, unsigned long,

    DISPATCH_BLOCK_BARRIER,

    DISPATCH_BLOCK_DETACHED,

    DISPATCH_BLOCK_ASSIGN_CURRENT,

    DISPATCH_BLOCK_NO_QOS_CLASS,

    DISPATCH_BLOCK_INHERIT_QOS_CLASS,

    DISPATCH_BLOCK_ENFORCE_QOS_CLASS

);

 

dispatch_block_create创建

# 在该函数中,flags 参数用来设置 block 的标记,block 参数用来设置具体的任务。

# flags 的类型为 dispatch_block_flags_t 的枚举,用于设置 block 的标记,定义如下

dispatch_block_t dispatch_block_create(dispatch_block_flags_t flags, dispatch_block_t block);

 

# 相比于 dispatch_block_create 函数,这种方式在创建 block 的同时可以指定了相应的优先级。

# dispatch_qos_class_t 是 qos_class_t 的别名

dispatch_block_t

dispatch_block_create_with_qos_class(dispatch_block_flags_t flags,

        dispatch_qos_class_t qos_class, int relative_priority,

        dispatch_block_t block);

 

 

qos_class_t 是一种枚举,有以下类型:

    QOS_CLASS_USER_INTERACTIVE:user interactive 等级表示任务需要被立即执行,用来在响应事件之后更新 UI,来提供好的用户体验。这个等级最好保持小规模。

    QOS_CLASS_USER_INITIATED:user initiated 等级表示任务由 UI 发起异步执行。适用场景是需要及时结果同时又可以继续交互的时候。

    QOS_CLASS_DEFAULT:default 默认优先级

    QOS_CLASS_UTILITY:utility 等级表示需要长时间运行的任务,伴有用户可见进度指示器。经常会用来做计算,I/O,网络,持续的数据填充等任务。这个任务节能。

    QOS_CLASS_BACKGROUND:background 等级表示用户不会察觉的任务,使用它来处理预加载,或者不需要用户交互和对时间不敏感的任务。

    QOS_CLASS_UNSPECIFIED:unspecified 未指明

 

代码

dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT); 

 

dispatch_block_t block = dispatch_block_create(0, ^{ NSLog(@"normal do some thing..."); }); 

dispatch_async(concurrentQuene, block); 

 

dispatch_block_t qosBlock = dispatch_block_create_with_qos_class(0, QOS_CLASS_DEFAULT, 0, ^{ 

    NSLog(@"qos do some thing..."); 

}); 

dispatch_async(concurrentQuene, qosBlock);

 

监听 block 执行结束   两种办法

有时我们需要等待特定的 block 执行完成之后,再去执行其他任务。有两种方法可以获取到指定 block 执行结束的时机。

 

dispatch_block_wait

 

long dispatch_block_wait(dispatch_block_t block, dispatch_time_t timeout);

 

#======================#======================#======================

 

 

# 如何使用

## 因为 dispatch_block_wait 会阻塞当前线程,所以不应该放在主线程中调用。创建一个新的并队列执行

dispatch_queue_t concurrentQuene = dispatch_queue_create("concurrentQuene", DISPATCH_QUEUE_CONCURRENT); 

dispatch_async(concurrentQuene, ^{ 

    dispatch_queue_t allTasksQueue = dispatch_queue_create("allTasksQueue", DISPATCH_QUEUE_CONCURRENT); 

 

    dispatch_block_t block = dispatch_block_create(0, ^{ 

        NSLog(@"开始执行"); 

        [NSThread sleepForTimeInterval:3]; NSLog(@"结束执行"); 

    }); 

    dispatch_async(allTasksQueue, block); 

 

    // 等待时长,10s 之后超时 

    dispatch_time_t timeout = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)); 

    long resutl = dispatch_block_wait(block, timeout); 

    if (resutl == 0) { 

        NSLog(@"执行成功"); 

    } else { 

        NSLog(@"执行超时"); 

    } 

});

 

dispatch_block_notify

第一个Block是操作的Block, 第二个Block是完成操作之后的Block

dispatch_block_notify(dispatch_block_t block, dispatch_queue_t queue,

        dispatch_block_t notification_block);

 

#======================#======================#======================

 

 

NSLog(@"---- 开始设置任务 ----"); 

dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL); 

 

// 耗时任务 

dispatch_block_t taskBlock = dispatch_block_create(0, ^{ 

    NSLog(@"开始耗时任务"); 

    [NSThread sleepForTimeInterval:2.f]; 

    NSLog(@"完成耗时任务"); 

}); 

 

dispatch_async(serialQueue, taskBlock); 

 

// 更新 UI 

dispatch_block_t refreshUI = dispatch_block_create(0, ^{ NSLog(@"更新 UI"); }); 

// 设置监听 

dispatch_block_notify(taskBlock, dispatch_get_main_queue(), refreshUI); 

NSLog(@"---- 完成设置任务 ----");

 

GCD的取消:dispatch_block_cancel

iOS8 后 GCD 支持对 dispatch block 的取消。

 

这个函数用异步的方式取消指定的 block。

取消操作使将来执行 dispatch block 立即返回,但是对已经在执行的 dispatch block 没有任何影响。

当一个 block 被取消时,它会立即释放捕获的资源。

如果要在一个 block 中对某些对象进行释放操作,在取消这个 block 的时候,需要确保内存不会泄漏。

 

# 取消当前已获取资源但尚未执行的Block

void dispatch_block_cancel(dispatch_block_t block);

 

# zero if not canceled. 下面的方法用来测试Block是否成功的取消了

long dispatch_block_testcancel(dispatch_block_t block);

 

#======================#======================#======================

 

dispatch_queue_t serialQueue = dispatch_queue_create("com.fyf.serialqueue", DISPATCH_QUEUE_SERIAL); 

// 耗时任务 

dispatch_block_t firstTaskBlock = dispatch_block_create(0, ^{ 

    NSLog(@"开始第一个任务"); 

    [NSThread sleepForTimeInterval:1.5f]; 

    NSLog(@"结束第一个任务"); 

}); 

 

// 耗时任务 

dispatch_block_t secTaskBlock = dispatch_block_create(0, ^{ 

    NSLog(@"开始第二个任务"); 

    [NSThread sleepForTimeInterval:2.f]; 

    NSLog(@"结束第二个任务"); 

}); 

 

dispatch_async(serialQueue, firstTaskBlock); 

dispatch_async(serialQueue, secTaskBlock); 

 

// 等待 1s,让第一个任务开始运行 

[NSThread sleepForTimeInterval:1]; 

dispatch_block_cancel(firstTaskBlock); 

NSLog(@"尝试过取消第一个任务"); 

dispatch_block_cancel(secTaskBlock); 

NSLog(@"尝试过取消第二个任务");

 

开始第一个任务

尝试过取消第一个任务

尝试过取消第二个任务

结束第一个任务

可以发现第二个任务可以顺利取消

 

 

dispatch source

dispatch源(dispatch source)和RunLoop源概念上有些类似的地方,而且使用起来更简单。要很好地理解dispatch源,其实把它看成一种特别的生产消费模式。dispatch源好比生产的数据,当有新数据时,会自动在dispatch指定的队列(即消费队列)上运行相应地block,生产和消费同步是dispatch源会自动管理的。

dispatch源的使用基本为以下步骤:

1. dispatch_source_t source = dispatch_source_create(dispatch_source_type, handler, mask, dispatch_queue);//创建dispatch源,这里使用加法来合并dispatch源数据,最后一个参数是指定dispatch队列

2. dispatch_source_set_event_handler(source, ^{ //设置响应dispatch源事件的block,在dispatch源指定的队列上运行

  //可以通过dispatch_source_get_data(source)来得到dispatch源数据

});

3. dispatch_resume(source); //dispatch源创建后处于suspend状态,所以需要启动dispatch源

4. dispatch_source_merge_data(source, value); //合并dispatch源数据,在dispatch源的block中,dispatch_source_get_data(source)就会得到value。

是不是很简单?而且完全不必编写同步的代码。比如网络请求数据的模式,就可以这样来写:

    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0,0, dispatch_get_global_queue(0, 0));

    dispatch_source_set_event_handler(source, ^{

        dispatch_sync(dispatch_get_main_queue(), ^{

    //更新UI

        });

    });

    dispatch_resume(source);

    dispatch_async(dispatch_get_global_queue(0, 0), ^{

   //网络请求

        dispatch_source_merge_data(source, 1); //通知队列

    });

dispatch源还支持其它一些系统源,包括定时器、监控文件的读写、监控文件系统、监控信号或进程等,基本上调用的方式原理和上面相同,只是有可能是系统自动触发事件。比如dispatch定时器:

dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

dispatch_source_set_timer(timer, dispatch_walltime(NULL, 0), 10*NSEC_PER_SEC, 1*NSEC_PER_SEC); //10秒触发timer,误差1

dispatch_source_set_event_handler(timer, ^{

  //定时处理

});

dispatch_resume(timer);

 

 

最后,dispatch源的其它一些函数大致罗列如下:

uintptr_t dispatch_source_get_handle(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第二个参数

unsignedlong dispatch_source_get_mask(dispatch_source_t source); //得到dispatch源创建,即调用dispatch_source_create的第三个参数

void dispatch_source_cancel(dispatch_source_t source); //取消dispatch源的事件处理--即不再调用block。如果调用dispatch_suspend只是暂停dispatch源。

long dispatch_source_testcancel(dispatch_source_t source); //检测是否dispatch源被取消,如果返回非0值则表明dispatch源已经被取消

void dispatch_source_set_cancel_handler(dispatch_source_t source, dispatch_block_t cancel_handler);//dispatch源取消时调用的block,一般用于关闭文件或socket等,释放相关资源

void dispatch_source_set_registration_handler(dispatch_source_t source, dispatch_block_tregistration_handler); //可用于设置dispatch源启动时调用block,调用完成后即释放这个block。也可在dispatch源运行当中随时调用这个函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值