3、GCD-Objc - Part 2

GCD 文档的查看
GCD 最全的资料不在百度,不在 api ,在 mac 终端。
打开终端

$ man dispatch
Snip20160623_4.png
// 退出当前界面
$ q 

see also
我们可以通过输入 man dispatch_after 查看相应的文档。

// dispatch_after 函数的文档
$ man dispatch_after

// dispatch_async 函数的文档
$ man dispatch_async

1、队列同步任务和异步任务在项目中的使用

假设: a 为登录, b, c 为不同的下载任务。
下面这样的操作就可以保证,只有在登录的情况下才可以进行 下载任务。

// 创建一个并发队列
dispatch_queue_t q = dispatch_queue_create("demo", DISPATCH_QUEUE_CONCURRENT);


// 并发队列同步执行
// 不会开线程,会在当前线程执行
// 同步执行任务 - 作用可以让一些异步执行的任务"依赖" 某一个特殊的任务
dispatch_sync(q, ^{
    NSLog(@"a: %@",[NSThread currentThread]);
});


// 永远都是在 a 执行完毕后,在回执行 b c ,但是 b ,  c 执行的顺序是不定的。


// 并发队列异步执行
// 会开线程,在子线程运行
dispatch_async(q, ^{
    NSLog(@"b: %@",[NSThread currentThread]);
});


dispatch_async(q, ^{
    NSLog(@"c: %@",[NSThread currentThread]);
});

打印结果:
2016-06-21 12:22:01.900 Thread-Objc[44983:2486117] a: <NSThread: 0x7f8762501ba0>{number = 1, name = main}
2016-06-21 12:22:01.900 Thread-Objc[44983:2486394] c: <NSThread: 0x7f876257ac90>{number = 3, name = (null)}
2016-06-21 12:22:01.900 Thread-Objc[44983:2486385] b: <NSThread: 0x7f8762717f20>{number = 2, name = (null)}

2、队列迭代

提交一个任务到队列进行多次执行

/*
  size_t iterations :         任务执行的次数
  dispatch_queue_t queue :   要执行任务的队列
  void (^block)(size_t) :    要执行的任务
*/
void
dispatch_apply(size_t iterations, dispatch_queue_t queue,
        void (^block)(size_t));


// 上面函数是下面的封装。
void
dispatch_apply_f(size_t iterations, dispatch_queue_t queue,
    void *context,
    void (*work)(void *, size_t));

如果 dispatch_queue_t queue 的队列是并发队列。任务是并发执行的, 它必须是可重入的安全。

dispatch_apply( ) 是同步执行的。 意味着会阻塞当前线程。和 for 循环一样只有在迭代完成后才会返回。如果需要异步执行,就需要创建一个新的 队列,调用 dispatch_async() 函数,再在 dispatch_async() 函数中调用 dispatch_apply( )。
示例:

 NSLog(@"before: %@", [NSThread currentThread]);
    
 __block int x = 0;

  // q 是串行或者并行没有多大的关系
 dispatch_queue_t q = dispatch_queue_create("q", DISPATCH_QUEUE_CONCURRENT);
    
 dispatch_async(q, ^{

        // q1 是串行的,就只开1条线程,并发的就开多条线程。线程数由 GCD 决定。
        dispatch_queue_t q1 = dispatch_queue_create("q1", DISPATCH_QUEUE_SERIAL);
        size_t iterations = 10;
        dispatch_apply( iterations, q1, ^(size_t idx ) {
            NSLog(@"size_t : %zd --- x : %zd -- %@", idx, x, [NSThread currentThread]);
        });
});
NSLog(@"after: %@", [NSThread currentThread]);

在主线程中调用 dispatch_apply( ) 传入的是主队列,会造成死锁。

文档中说:
dispatch_apply() 是 dispatch_async() 和 semaphore (完成信号)的包装;

(我特么真没看出来)
dispatch_apply() 和 dispatch_async() 不一样的地方是,block 提交到 dispatch_apply() 是相对独立的,或者是依赖于已经完成但是 index 比自己小的 block 。

这个函数就像 for 循环一样提供数据级并发。 相对于 for 循环(for 循序操作都是在当前线程),并发迭代 的效率更加的高。

使用前提条件:
本次的迭代执行的 block 和上一次迭代执行的 block 不能有任何的关系。
(每次 block 的迭代执行是相对独立的!。 如果说执行的 block 之间有依赖关系,那你还是放弃吧!)

如果你使用循环执行固定次数的迭代, 并发dispatch queue可能会提高性能。

  • 如果每次迭代执行的任务与其它迭代独立无关,而且循环迭代执行顺序也无关紧要的话,你可以调用dispatch_apply或dispatch_apply_f函数来替换循环。

普通 for 循环

NSLog(@"before: %@", [NSThread currentThread]);
int x = 0;
for (int index = 0 ; index < 10; index ++) {
        NSLog(@"task: %@  --- index : %zd  ---- x : %zd", [NSThread currentThread], index , x );
        x++;
}
    
NSLog(@"after: %@", [NSThread currentThread]);

打印结果:
2016-06-23 14:55:04.706 Thread-Objc[12389:1352331] before: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
2016-06-23 14:55:04.707 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}  --- index : 0  ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)}  --- index : 1  ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)}  --- index : 2  ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)}  --- index : 3  ---- x : 0
2016-06-23 14:55:04.708 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}  --- index : 4  ---- x : 1
2016-06-23 14:55:04.708 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)}  --- index : 5  ---- x : 2
2016-06-23 14:55:04.708 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)}  --- index : 6  ---- x : 3
2016-06-23 14:55:04.709 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)}  --- index : 7  ---- x : 4
2016-06-23 14:55:04.709 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}  --- index : 8  ---- x : 5
2016-06-23 14:55:04.710 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)}  --- index : 9  ---- x : 6
2016-06-23 14:55:04.721 Thread-Objc[12389:1352331] after: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}

GCD 迭代

NSLog(@"before: %@", [NSThread currentThread]);

size_t iterations = 10;
__block int x = 0;
dispatch_apply( iterations , dispatch_get_global_queue(0, 0), ^(size_t  idx) {
    NSLog(@"task: %@  --- index : %zd  ---- x : %zd", [NSThread currentThread], idx , x );
    x++;
});

NSLog(@"after: %@", [NSThread currentThread]);

打印结果
2016-06-23 14:55:04.706 Thread-Objc[12389:1352331] before: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}
2016-06-23 14:55:04.707 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}  --- index : 0  ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)}  --- index : 1  ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)}  --- index : 2  ---- x : 0
2016-06-23 14:55:04.707 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)}  --- index : 3  ---- x : 0
2016-06-23 14:55:04.708 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}  --- index : 4  ---- x : 1
2016-06-23 14:55:04.708 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)}  --- index : 5  ---- x : 2
2016-06-23 14:55:04.708 Thread-Objc[12389:1352381] task: <NSThread: 0x7f97f2635530>{number = 3, name = (null)}  --- index : 6  ---- x : 3
2016-06-23 14:55:04.709 Thread-Objc[12389:1352375] task: <NSThread: 0x7f97f2635970>{number = 4, name = (null)}  --- index : 7  ---- x : 4
2016-06-23 14:55:04.709 Thread-Objc[12389:1352331] task: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}  --- index : 8  ---- x : 5
2016-06-23 14:55:04.710 Thread-Objc[12389:1352369] task: <NSThread: 0x7f97f2626000>{number = 2, name = (null)}  --- index : 9  ---- x : 6
2016-06-23 14:55:04.721 Thread-Objc[12389:1352331] after: <NSThread: 0x7f97f2402dd0>{number = 1, name = main}

对比发现 x 的值在 GCD 迭代的过程中是凌乱的。
当符合前提条件,GCD 的迭代的效率更高。

3、队列的暂停和启动操作

给目标队列添加管理对象

/*
  dispatch_object_t object : 队列管理对象
  dispatch_queue_t queue : 被管理的队列

*/
void
dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);

调度对象的相关知识
调度对象是通过共享函数去协调内存管理,暂停,取消,上下文指针。

  • 挂起操作
    在调度队列或者调度源被触发的 blocks 能够通过分别执行 dispatch_suspend()dispatch_resume() 函数进行挂起和启动操作。另一些调度对象是不支持挂起操作的。
    这个调度框架总是在执行 block 之前检查 block 的悬挂状态。一个 block 在执行期间对 block 做的改变是不会产生效果的。因此一个对象的挂起操作是异步的,除非它是通过执行上下文从目标队列获取的对象。把一个不是调度队列或调度源的对象执行挂起操作或启用操作的操作结果是没有定义的。

重点:
悬挂操作适用于调度对象生命周期的任何时候。包括 finalizer 函数,取消回调。 挂起操作会使计数器加1 ,启用操作会使计数器减一。因此, dispatch_suspend()dispatch_resume()进行成对调用,以保证对象最终能够释放。一个调度对象在所有的引用释放后进行挂起操作的操作结果是没有定义的。

  • 上下文指针
    调度对象支持补充(追加)上下文指针。 上下文指针的值能够分别通过 dispatch_get_context()dispatch_set_context() 函数进行获取和更新。 当调度对象所有的引用全部释放的时候,如果上下文指针是非空的,通过dispatch_set_finalizer_f() 能给每一个可选的对象指定一个可以异步调用的终结函数。给应用程序一个可以给调度自由关联上下文数据的机会。这个终结器将要运行在和 调度对象绑定的目标队列上。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容