2018-09-20 10:15:42 annkie 阅读数 151

dispatch_sync

  • 同步执行,直到执行的任务完成才返回
  • 目标队列:串行队列
    在当前线程执行任务,顺序执行
  • 目标队列:并发队列
    新建线程执行任务,顺序执行
  • 目标队列:主线程队列
    在主线程执行任务,顺序执行
  • 在当前线程调用且目标队列为当前队列时,形成死锁
  • 相应的block不会被copy
  • 目标队列不会被系统retain
  • 在尽可能的情况下,block将被优化运行在当前线程。

dispatch_barrier_sync

  • 同步执行,直到执行的任务完成才返回
  • 目标队列:串行队列
    当前线程执行任务,顺序执行
  • 目标队列:并发队列
    当前线程执行任务,顺序执行
  • 目标队列:主线程队列
    在主线程执行任务,顺序执行
  • 在当前线程调用且目标队列为当前队列时,形成死锁
  • 相应的block不会被copy
  • 目标队列不会被系统retain

  • 在尽可能的情况下,barrier block将被优化运行在当前线程。
  • 当barrier block到达自定义并发队列的顶部,它不会立刻执行,相反需要等到当前执行block完成,然后barrier block自动运行。在barrier block之后的block必须等到barrier block执行完毕才执行,形成一个栅栏。
    因此可以用来等待多个block任务完成的时机。
  • 必须由 dispatch_queue_create创建自定义的并发队列。如果不是由该函数创建的并发队列,而是串行队列(主队列)或全局并发队列,其行为等同 dispatch_sync,失去栅栏的作用。

dispatch_async

  • 异步执行,立刻返回
  • 目标队列:串行队列,顺序执行
    新建线程执行任务
  • 目标队列:并发队列,并行执行
    新建线程执行任务
  • 目标队列:主线程队列
    在主线程执行任务

  • 相应的block会被copy
  • 目标队列被系统retain直到block执行完毕

dispatch_barrier_async

  • 异步执行,立刻返回
  • 目标队列:串行队列,顺序执行
    新建线程执行任务
  • 目标队列:并发队列,并行执行
    新建线程执行任务
  • 目标队列:主线程队列
    在主线程执行任务

  • 相应的block会被copy

  • 目标队列被系统retain直到block执行完毕

  • 当barrier block到达自定义并发队列的顶部,它不会立刻执行,相反需要等到当前执行block完成,然后barrier block自动运行。在barrier block之后的block必须等到barrier block执行完毕才执行,形成一个栅栏。
    因此可以用来等待多个block任务完成的时机。

  • 必须由 dispatch_queue_create创建自定义的并发队列。如果不是由该函数创建的并发队列,而是串行队列(主队列)或全局并发队列,其行为等同 dispatch_async,失去栅栏的作用。

dispatch_group_async

发送一个block任务到派发队列并且关联到一个派发group

  • 异步调用,立刻返回
  • group将被系统retain直到block执行完毕
  • queue将被系统retain直到block执行完毕
  • block将被copy到堆上
  • 采用dispatch_group_notify可以用来等待一组block任务执行完毕。

dispatch_group_notify

将一个block对象计划到派发队列,当所有已经提交的block任务对象执行完毕后执行该block。

  • 用来实现一系列任务的完成通知功能。
  • 如果group为nil,该block将直接运行
  • group可以通过dispatch_release释放或者继续被重用。
  • DispatchGroup对象也可以实现相同功能。
2018-03-15 14:45:01 ycf03211230 阅读数 1659

前言:Queue队列分为几下几种
1、全局队列:dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
2、主队列:dispatch_get_main_queue()
3、用户创建的串行队列:
dispatch_queue_create(“com.yuna.com”, DISPATCH_QUEUE_SERIAL/NULL);
4、用户创建的并行队列:
dispatch_queue_create(“com.yuna.com”, DISPATCH_QUEUE_CONCURRENT);

同步与异步的区别:dispatch_sync 和 dispatch_async

dispatch_sync(queue,block):同步队列,dispatch_sync 函数不会立即返回,会阻塞当前线程,将block放到指定的queue上面执行,等待该block同步执行完成后才会返回

dispatch_async(queue,block):异步队列,dispatch_async 函数会立即返回,block放到指定queue中等待执行,该block是并行还是串行只跟queue定义有关

……………………………………………………………………………………
一个NSThread线程对象都可以有它对应的名字(name)和编号(number). 名字可以通过NSThread的name属性来设置
编号(number)是不能设置的,一般由系统决定,但主线程默认就是1,只要不为1的就是子线程

我们看下他们的几种组合情况
1、串行队列+同步

/*
 串行队列 + 同步任务:没有开启新线程,任务是逐个完成的
 */
- (void)serialSync{
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_SERIAL);
//    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", NULL);
    //在队列里添加同步任务
    dispatch_sync(queue, ^{
        for(int  i=0 ; i<10;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for(int  i=0 ; i<10;i++){
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        for(int  i=0 ; i<10;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });
}

我们看下打印的结果:都是在同一个线程执行,没有开启新线程,而且是任务逐个执行的
这里写图片描述

2、 串行队列 + 异步

/*
 串行队列 + 异步任务:开启新线程,任务是逐个完成的
 */
- (void)serialAsync{
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_SERIAL);
    //    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", NULL);
      NSLog(@"串行队列 + 异步----%@",[NSThread currentThread]);
    //在队列里添加异步任务
    dispatch_async(queue, ^{ 
        for(int  i=0 ; i<10;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int  i=0 ; i<10;i++){
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for(int  i=0 ; i<10;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });
}

我们看下打印的结果:有个主线程,还开启了一个新的进程,任务是逐一执行的
这里写图片描述
3、 并发队列 + 同步

/*
 并发队列 + 同步任务 :没有开启新线程,任务是逐个执行
 */
-(void)createConCurrentQueueSyn{
    //创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_CONCURRENT);
     NSLog(@"并发队列 + 同步----%@",[NSThread currentThread]);
    //在队列里添加同步任务
    dispatch_sync(queue, ^{
       
        for(int  i=0 ; i<count;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });

    
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });
}

我们看下打印的结果:都是在同一个线程中,没有开启一个新的进程,任务是逐一执行的
这里写图片描述
4、 并发队列 + 异步

/*
 并发队列 + 异步任务 :开启新线程,任务是并发的
 
 --1--<NSThread: 0x7fe399724ba0>{number = 2, name = (null)}
 --2--<NSThread: 0x7fe399710790>{number = 3, name = (null)}
 --3--<NSThread: 0x7fe39972c110>{number = 4, name = (null)}
 */

-(void)createConCurrentQueueAsyn{
    
    dispatch_queue_t queue = dispatch_queue_create("com.pi2e.com", DISPATCH_QUEUE_CONCURRENT);
     NSLog(@"并发队列 + 异步----%@",[NSThread currentThread]);
    //队列中添加异步任务
    dispatch_async(queue, ^{
       
        for (int i=0; i<5; i++) {
            NSLog(@"--1--%@",[NSThread currentThread]);
        }
    });
    
    
    dispatch_async(queue, ^{
        
        for (int i=0; i<5; i++) {
            NSLog(@"--2--%@",[NSThread currentThread]);
        }
    });

    dispatch_async(queue, ^{
        
        for (int i=0; i<5; i++) {
            NSLog(@"--3--%@",[NSThread currentThread]);
        }
    });
}

我们看下打印的结果:正常打印的是在主线程中执行,而dispatch_async中是block是在开启的新线程中并发执行的
这里写图片描述
5、全局队列 + 同步

/*
 全局队列 + 同步任务:,没有开启新线程,任务逐个执行
 */

-(void)GlabolSyc{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
     NSLog(@"全局队列 + 同步----%@",[NSThread currentThread]);
    
    //在队列里添加同步任务
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });
    
    
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });

}

我们看下打印的结果:都是在同一个线程中执行的,并且是逐个执行
这里写图片描述
6、全局 + 异步

/*
 全局 + 异步:开启新线程,任务是并发的
 */

- (void)GlobalAsync{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
     NSLog(@"全局队列 + 异步----%@",[NSThread currentThread]);
    //队列中添加异步任务
    dispatch_async(queue, ^{
        
        for (int i=0; i<5; i++) {
            NSLog(@"--1--%@",[NSThread currentThread]);
        }
    });
    
    
    dispatch_async(queue, ^{
        
        for (int i=0; i<5; i++) {
            NSLog(@"--2--%@",[NSThread currentThread]);
        }
    });
    
    
    
    dispatch_async(queue, ^{
        
        for (int i=0; i<5; i++) {
            NSLog(@"--3--%@",[NSThread currentThread]);
        }
    });
}

我们看下打印的结果:正常打印的是在主线程中执行,而dispatch_async中是block是在开启的新线程中并发执行的
这里写图片描述
7、 主队列+同步:运行后直接crash

/*
 主队列(串行)+同步:界面卡死
 主队列中添加同步任务会造成死锁的
 */

- (void)mainthreadSync{
    
    dispatch_queue_t queue = dispatch_get_main_queue();
     NSLog(@"主队列(串行)+同步----%@",[NSThread currentThread]);
    //在队列里添加同步任务
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<10;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<10;i++){
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });
    dispatch_sync(queue, ^{
        
        for(int  i=0 ; i<10;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });
}

8、主队列+异步

/*
 主队列(串行)+异步:没有创建新线程,任务逐个完成 {都是在主线程中执行的}
 */
- (void)mainthreadAsync{
    
    dispatch_queue_t queue = dispatch_get_main_queue();
     NSLog(@"主队列(串行)+异步----%@",[NSThread currentThread]);
    //在队列里添加异步任务
    dispatch_async(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---1--%@",[NSThread currentThread]);
        }
    });
    
    dispatch_async(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---2--%@",[NSThread currentThread]);
        }
    });
    
    
    dispatch_async(queue, ^{
        
        for(int  i=0 ; i<count;i++){
            NSLog(@"---3--%@",[NSThread currentThread]);
        }
    });
}

我们看下打印的结果:都是在主线程中,逐一执行的
这里写图片描述

总结:
任务:执行什么操作
同步:只能在当前线程中执行任务,不具备开启新线程的能力
异步:可以在新的线程中执行任务,具备开启新线程的能力
主要区别:能否开启新的线程
………………………………………………………………
队列:用来存放任务 (FIFO)
并行队列:1、可以让多个任务并发(同时)执行(自动开启多个线程 同时执行任务)
2、并发的功能只有在异步函数下才有效
方式一:dispatch_queue_create(“com.pi2e.com”,DISPATCH_QUEUE_CONCURRENT);
方式二:dispatch_get_global_queue
……………………………………………………………………
串行队列:让任务一个接着一个的执行(也就是说必须等一个任务执行完毕后才可执行下一个任务)
方式一:dispatch_create_queue(“com.pi2e.com”,DISPATCH_QUEUE_SEARIAL/NULL)
方式二:dispatch_get_main_queue();
注意:主队列是GCD自带的一种特殊的串行队列,放置到主队列的任务,都会在主线程中执行
主要区别:任务的执行方式不同

2014-11-14 16:36:30 backpackcsdn 阅读数 326

func dispatch_async(_ queuedispatch_queue_t!, _ blockdispatch_block_t!)

dispatch_queue_t为线程队列,异步的方式将block要执行的代码添加到_queue队列中,等待执行。

1.后台执行

dispatch_async(dispatch_get_global_queue(0, 0), ^{
      // something
 });

这里func dispatch_get_global_queue(_ identifierInt_ flagsUInt) -> dispatch_queue_t!

identifier是提供给要执行代码到队列里的服务质量,决定优先级;flags留作以后用,一般置0.

2.主程序执行

dispatch_async(dispatch_get_main_queue(), ^{
      // something
 });
3.执行一次

static dispatch_once_t onceToken;
 dispatch_once(&onceToken, ^{
     // code to be executed once
 });


2015-07-18 20:38:03 mlibai 阅读数 5330

一,兴趣是最好的老师。

在IOS开发中,为保证单例在整个程序运行中只被初始化一次,单线程的时候,通过静态变量可以实现;但是多线程的出现,使得在极端条件下,单例也可能返回了不同的对象。如在单例初始化完成前,多个进程同时访问单例,那么这些进程可能都获得了不同的单例对象。

苹果提供了 dispatch_once(dispatch_once_t *predicate,dispatch_block_t block);函数来避免这个问题,作为程序员,应该都会对这个函数的实现方法很感兴趣。网上搜索了一下,有不少相关的帖子,看完我还是一头雾水。有帖子在说到这个问题时,还列出了汇编代码,然而这只是让我这不懂汇编的更加迷茫。然而,经过几个小时的搜索和思考,虽然没有弄明白dispatch_once函数是怎么实现的,但是总算把头文件里列出的内容弄清楚了。

多线程保护下的单例初始化代码:

+ (instancetype)defaultObject {
    static SharedObject *sharedObject = nil;
    static dispatch_once_t predicate;
    dispatch_once(&predicate, ^{
        sharedObject = [[SharedObject alloc] init];
    });
    return sharedObject;
}

通过查阅头文件,我们很容易知道 dispatch_once_t 就是 long 型,即长整型。静态变量在程序运行期间只被初始化一次,然后其在下一次被访问时,其值都是上次的值,其在除了这个初始化方法以外的任何地方都不能直接修改这两个变量的值。这是单例只被初始化一次的前提。

然后就是最神秘的 dispatch_once 函数了,如何才能保证,两个同时调用这个方法的进程,只执行一次这个函数的block块呢?看看头文件里,有没有什么说明。

首先定位到的是 :

#undef dispatch_once
#define dispatch_once _dispatch_once

这段代码的意思是,先取消 dispatch_once 的定义, 然后把 _dispatch_once 定义为 dispatch_once。这是在干什么? 在定位一下 _dispatch_once 这个函数,就在这句话的上面,完整的一段代码贴出来。

#ifdef __BLOCKS__
__OSX_AVAILABLE_STARTING(__MAC_10_6,__IPHONE_4_0)
DISPATCH_EXPORT DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
dispatch_once(dispatch_once_t *predicate, dispatch_block_t block);

DISPATCH_INLINE DISPATCH_ALWAYS_INLINE DISPATCH_NONNULL_ALL DISPATCH_NOTHROW
void
_dispatch_once(dispatch_once_t *predicate, dispatch_block_t block)
{
    if (DISPATCH_EXPECT(*predicate, ~0l) != ~0l) {
        dispatch_once(predicate, block);
    }
}
#undef dispatch_once
#define dispatch_once _dispatch_once
#endif

对于宏定义不熟悉的同学来说,这一堆宏定义,简直根乱码一样。上面这段代码的意思大概是,先声明了 dispatch_once 函数,下面又实现了 _dispatch_once 函数。所以上面那个先取消又定义的宏,应该是:用户调用  dispatch_once 函数,实际上调用的是 _dispatch_once 函数;而真正的 dispatch_once 函数是在 _dispatch_once 内调用的。绕个圈这么做,是为什么 ?继续分析。

通过分析 _dispatch_once 函数,除了 DISPATCH_EXPECT 这个方法外,别的都很正常,那么就看下这个东西是个啥。(完全新手可能不懂 ~0l 是啥,这个意思长整型0按位取反,其实就是长整型的-1)。

#if __GNUC__
#define DISPATCH_EXPECT(x, v) __builtin_expect((x), (v))
#else
#define DISPATCH_EXPECT(x, v) (x)
#endif

__GNUC__是啥?好像是编译器相关的,不作深究。#define DISPATCH_EXPECT(x, v) (x) 但是这个的意思很明显,就是如果没有定义__GNUC__的话 DISPATCH_EXPECT(x, v)  就是第一个参数 (x)。

百度一下后得知,对于 __builtin_expect ,就是告诉编译器,它的第一个参数的值,在very very very很大的情况下,都会是第二个参数。
好吧,现在回到 _dispatch_once 函数,再看它的意思: DISPATCH_EXPECT(*predicate, ~0l)  就是说,*predicate 很可能是 ~0l ,而当  DISPATCH_EXPECT(*predicate, ~0l)  不是 ~0! 时 才调用真正的 dispatch_once 函数。

这是啥情况,这段代码好像根如何保证代码只被执行一次并没有什么关系!细分析之,第一次运行,predicate的值是默认值0,按照逻辑,如果有两个进程同时运行到 dispatch_once 方法时,这个两个进程获取到的 predicate 值都是0,那么最终两个进程都会调用 最原始那个 dispatch_once 函数!!!

代码绕来绕去,研究半天,竟然最后还是会同时调用 dispatch_once 函数,既然如此,上面哪些代码意义何在?!

网上看了不少帖子,关于多线程保护的逻辑,都只是分析了 _dispatch_once 函数,然而这并没有用。

在我看来,头文件里列出的内容,并不是 dispatch_once 实现多线程保护的逻辑,而是编译优化逻辑。也就是告诉编译器,在调用 dispatch_once 时,绝大部分情况不用调用原始的 dispatch_once ,而是直接运行后续的内容。

所以真正的实现的多线程保护逻辑,苹果并没有展示给我们,封装在原始的 dispatch_once 函数的实现里,里面应该有关于进程锁类似的机制,保证某段代码在即使有多个线程同时访问时,只有一个线程被执行。既然真正的逻辑并没有展示,那就没有深究下去了,苹果说这个函数是只能被执行一次,我们使用就是了。

那么在这里,我其实也可以猜测,predicate的数值,肯定在block运行后被更改为 ~0l ,即 -1,可以用下面的代码测试一下。

+ (instancetype)defaultObject{
    static SharedObject *sharedObject = nil;
    static dispatch_once_t predicate;
    NSLog(@"在dispatch_once前:%ld", predicate);
    dispatch_once(&predicate, ^{
	 NSLog(@"在dispatch_once中:%ld", predicate);
         sharedObject = [[SharedObject alloc] init]; 
    });
    NSLog(@"在dispatch_once后:%ld", predicate);
    return sharedObject;
}


执行结果:

在dispatch_once前:0
在dispatch_once中:140734607350288
在dispatch_once后:-1

二、dispatch_once是否真的线程安全?

非常极端的情况下,dispatch_once会发生死锁,例如下面这个例子,设计目的是两个单例互相依赖,一个初始化了,另一个也要初始化。

@interface TestA : NSObject
+ (TestA *)sharedInstance;
@end

@interface TestB : NSObject
+ (TestB *)sharedInstance;
@end

@implementation TestA

+ (TestA *)sharedInstance {
    static TestA *testA = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        testA = [[TestA alloc] init];
        [TestB shareInstanceB];
    });
    return testA;
}

@end

@implementation TestB

+ (TestB *)sharedInstance {
    static TestB *testB = nil;
    static dispatch_once_t token;
    dispatch_once(&token, ^{
        testB = [[TestB alloc] init];
        [TestA shareInstanceA];
    });
    return testB;
}

@end

当获取任何一个单例的时候,都会造成程序卡死。实际上,任何造成线程进入了互相等待状态的操作,都会导致线程死锁,上面的两个互相依赖的单例只是一个特例。在规范的业务员设计中要避免上面情况,但是如果业务中真的需要这样的设计,那么可以使用锁来解决。

#import <objc/objc-sync.h>

@implementation TestA

+ (TestA *)sharedInstance {
    static TestA *_shared = nil;
    if (_shared != nil) {
        return _shared;
    }
    objc_sync_enter(self);
    if (_shared != nil) {
        objc_sync_exit(self);
        return _shared;
    }
    _shared = [[TestA alloc] init];
    [TestB sharedInstance];
    objc_sync_exit(self);
    return _shared;
}

@end

@implementation TestB

+ (TestB *)sharedInstance {
    static TestB *_shared = nil;
    if (_shared != nil) {
        return _shared;
    }
    objc_sync_enter(self);
    if (_shared != nil) {
        objc_sync_exit(self);
        return _shared;
    }
    _shared = [[TestB alloc] init];
    [TestA sharedInstance];
    objc_sync_exit(self);
    return _shared;
}

@end

 

2016-04-19 13:29:36 xlym33 阅读数 185

iOS多线程——Dispatch Source

使用 Dispatch Source 而不使用 dispatch_async 的唯一原因就是利用联结的优势。

联结的大致流程:在任一线程上调用它的的一个函数 dispatch_source_merge_data 后,会执行 Dispatch Source 事先定义好的句柄(可以把句柄简单理解为一个 block )。

这个过程叫 Custom event ,用户事件。是 dispatch source 支持处理的一种事件。

简单地说,这种事件是由你调用 dispatch_source_merge_data 函数来向自己发出的信号。

一、创建dispatch源

dispatch_source_t source = dispatch_source_create(dispatch_source_type_t type, uintptr_t handle, unsigned long mask, dispatch_queue_t queue)

参数:

参数 意义
type dispatch源可处理的事件
handle 可以理解为句柄、索引或id,假如要监听进程,需要传入进程的ID
mask 可以理解为描述,提供更详细的描述,让它知道具体要监听什么
queue 自定义源需要的一个队列,用来处理所有的响应句柄(block)

Dispatch Source可处理的所有事件

名称 内容
DISPATCH_SOURCE_TYPE_DATA_ADD 自定义的事件,变量增加
DISPATCH_SOURCE_TYPE_DATA_OR 自定义的事件,变量OR
DISPATCH_SOURCE_TYPE_MACH_SEND MACH端口发送
DISPATCH_SOURCE_TYPE_MACH_RECV MACH端口接收
DISPATCH_SOURCE_TYPE_PROC 进程监听,如进程的退出、创建一个或更多的子线程、进程收到UNIX信号
DISPATCH_SOURCE_TYPE_READ IO操作,如对文件的操作、socket操作的读响应
DISPATCH_SOURCE_TYPE_SIGNAL 接收到UNIX信号时响应
DISPATCH_SOURCE_TYPE_TIMER 定时器
DISPATCH_SOURCE_TYPE_VNODE 文件状态监听,文件被删除、移动、重命名
DISPATCH_SOURCE_TYPE_WRITE IO操作,如对文件的操作、socket操作的写响应

注意:

  • DISPATCH_SOURCE_TYPE_DATA_ADD
    当同一时间,一个事件的的触发频率很高,那么Dispatch Source会将这些响应以ADD的方式进行累积,然后等系统空闲时最终处理,如果触发频率比较零散,那么Dispatch Source会将这些事件分别响应。

  • DISPATCH_SOURCE_TYPE_DATA_OR 和上面的一样,是自定义的事件,但是它是以OR的方式进行累积

二、一些函数

dispatch_suspend(queue) //挂起队列

dispatch_resume(source) //分派源创建时默认处于暂停状态,在分派源分派处理程序之前必须先恢复

dispatch_source_merge_data //向分派源发送事件,需要注意的是,不可以传递0值(事件不会被触发),同样也不可以传递负数。

dispatch_source_set_event_handler //设置响应分派源事件的block,在分派源指定的队列上运行

dispatch_source_get_data //得到分派源的数据

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

unsigned long 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_t registration_handler); //可用于设置dispatch源启动时调用block,调用完成后即释放这个block。也可在dispatch源运行当中随时调用这个函数。

三、代码

1. Dispatch Source的基础用法
    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); //通知队列
    });
2. Dispatch Source例子

上面的例子创建一个source,source的type为ADD的方式,然后将事件触发后要执行的句柄添加到main队列里,在source创建后默认是挂起的,需要用dispatch_resume函数来恢复监听,后面为了测试监听,加入了一个for循环,用dispatch_source_merge_data来触发事件,但是在触发事件的响应句柄里我们只打印了一次,结果是每次相加的和,也就是10,而不是打印了4次。

原因:DISPATCH_SOURCE_TYPE_DATA_ADD是将所有触发结果相加,最后统一执行响应,但是加入sleepForTimeInterval后,如果interval的时间越长,则每次触发都会响应,但是如果interval的时间很短,则会将触发后的结果相加后统一触发。

这在更新UI时很有用,比如更新进度条时,没必要每次触发都响应,因为更新时还有其他的用户操作(用户输入,触碰等),所以可以统一触发


    //创建source,以DISPATCH_SOURCE_TYPE_DATA_ADD的方式进行累加,而DISPATCH_SOURCE_TYPE_DATA_OR是对结果进行二进制或运算
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());

    //事件触发后执行的句柄
    dispatch_source_set_event_handler(source,^{

        NSLog(@"监听函数:%lu",dispatch_source_get_data(source));

    });

    //开启source
    dispatch_resume(source);

    dispatch_queue_t myqueue =dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(myqueue, ^ {

        for(int i = 1; i <= 4; i ++){

            NSLog(@"~~~~~~~~~~~~~~%d", i);

            //触发事件,向source发送事件,这里i不能为0,否则触发不了事件
            dispatch_source_merge_data(source,i);

            //当Interval的事件越长,则每次的句柄都会触发
            //[NSThread sleepForTimeInterval:0.0001];
        }
    });
3. 使用timer定时器

注意:

dispatch_source_set_timer(dispatch_source_t source, dispatch_time_t start, uint64_t interval, uint64_t leeway)
  • 参数:
  • source 分派源
  • start 数控制计时器第一次触发的时刻。参数类型是 dispatch_time_t,这是一个opaque类型,我们不能直接操作它。我们得需要 dispatch_time 和 dispatch_walltime 函数来创建它们。另外,常量 DISPATCH_TIME_NOW 和 DISPATCH_TIME_FOREVER 通常很有用。
  • interval 间隔时间
  • leeway 计时器触发的精准程度

下面代码可用作开屏倒计时


    //倒计时时间
    __block int timeout = 3;

    //创建队列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //创建timer
    dispatch_source_t _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

    //设置1s触发一次,0s的误差
    dispatch_source_set_timer(_timer,dispatch_walltime(NULL, 0),1.0*NSEC_PER_SEC, 0); //每秒执行

    //触发的事件
    dispatch_source_set_event_handler(_timer, ^{

        if(timeout<=0){ //倒计时结束,关闭

            //取消dispatch源
            dispatch_source_cancel(_timer);

        }
        else{

            timeout--;

            dispatch_async(dispatch_get_main_queue(), ^{

                //更新主界面的操作

                NSLog(@"~~~~~~~~~~~~~~~~%d", timeout);

            });
        }
    });

    //开始执行dispatch源
    dispatch_resume(_timer);
5. dispatch_suspend相关

    //创建DISPATCH_QUEUE_SERIAL队列
    dispatch_queue_t queue1 = dispatch_queue_create("com.iOSChengXuYuan.queue1", 0);
    dispatch_queue_t queue2 = dispatch_queue_create("com.iOSChengXuYuan.queue2", 0);

    //创建group
    dispatch_group_t group = dispatch_group_create();

    //异步执行任务
    dispatch_async(queue1, ^{
        NSLog(@"任务 1 : queue 1...");
        sleep(1);
        NSLog(@":white_check_mark:完成任务 1");
    });

    dispatch_async(queue2, ^{
        NSLog(@"任务 1 : queue 2...");
        sleep(1);
        NSLog(@":white_check_mark:完成任务 2");
    });

    //将队列加入到group
    dispatch_group_async(group, queue1, ^{
        NSLog(@":no_entry_sign:正在暂停 1");
        dispatch_suspend(queue1);
    });

    dispatch_group_async(group, queue2, ^{
        NSLog(@":no_entry_sign:正在暂停 2");
        dispatch_suspend(queue2);
    });

    //等待两个queue执行完毕后再执行
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"=======等待两个queue完成, 再往下进行...");

    //异步执行任务
    dispatch_async(queue1, ^{
        NSLog(@"任务 2 : queue 1");
    });
    dispatch_async(queue2, ^{
        NSLog(@"任务 2 : queue 2");
    });
    NSLog(@":red_circle:为什么这个NSLog会在上面两个NSLog之前打印:question::question:答:dispatch_suspend的作用:bangbang:");

    dispatch_resume(queue1);
    dispatch_resume(queue2);

    //1、当将 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 注释后,会产生崩溃,因为queue1没有挂起,但是却恢复了队列

    //2、如果同时将 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 和 dispatch_resume(queue1)注释,也会产生崩溃,但是原因(???)

    //3、将 dispatch_group_wait(group, DISPATCH_TIME_FOREVER); 和 dispatch_resume(queue1) 和 dispatch_suspend(queue1); 正常运行
6. 进度条例子
    //1、指定DISPATCH_SOURCE_TYPE_DATA_ADD,做成Dispatch Source(分派源)。设定Main Dispatch Queue 为追加处理的Dispatch Queue
    dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_DATA_ADD, 0, 0, dispatch_get_main_queue());

    __block NSUInteger totalComplete = 0;

    dispatch_source_set_event_handler(source, ^{

        //当处理事件被最终执行时,计算后的数据可以通过dispatch_source_get_data来获取。这个数据的值在每次响应事件执行后会被重置,所以totalComplete的值是最终累积的值。
        NSUInteger value = dispatch_source_get_data(source);

        totalComplete += value;

        NSLog(@"进度:%@", @((CGFloat)totalComplete/100));

        NSLog(@":large_blue_circle:线程号:%@", [NSThread currentThread]);
    });

    //分派源创建时默认处于暂停状态,在分派源分派处理程序之前必须先恢复。
    dispatch_resume(source);

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //2、恢复源后,就可以通过dispatch_source_merge_data向Dispatch Source(分派源)发送事件:
    for (NSUInteger index = 0; index < 100; index++) {

        dispatch_async(queue, ^{

            dispatch_source_merge_data(source, 1);

            NSLog(@":recycle:线程号:%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);

            usleep(20000);//0.02秒

        });
    }

    //3、比较上面的for循环代码,将dispatch_async放在外面for循环的外面,打印结果不一样
    //dispatch_async(queue, ^{
    //
    //    for (NSUInteger index = 0; index < 100; index++) {
    //
    //        dispatch_source_merge_data(source, 1);
    //
    //        NSLog(@":recycle:线程号:%@~~~~~~~~~~~~i = %ld", [NSThread currentThread], index);
    //
    //        usleep(20000);//0.02秒
    //    }
    //});

参考:

iOS Dispatch

阅读数 27

iOS GCD dispatch

博文 来自: fyanyan

iOS dispatch_semaphore_t

阅读数 422

没有更多推荐了,返回首页