GCD Thread

1. GCD的两个核心概念是:任务队列.

任务与队列

  • 任务 : 在block中执行的代码块
  • 队列 : 用来存放任务的

队列 和 线程的区别:

队列中存放的任务最后都要由线程来执行! 队列的原则:先进先出,后进后出

队列分类:

1.串行队列 2.并发队列 3.主队列 4.全局队列

  • 串行队列:任务一个接一个的执行
  • 并发队列:队列中的任务并发执行
  • 主队列:跟主线程相关的队列,主队列里面的内容都会在主线程中执行
  • 全局队列:一个特殊的并发队列

并发队列和全局队列的区别:

  • 并发队列有名称,可以跟踪错误.全局队列没有.
  • 在ARC中两个队列不需要考虑释放内存,但是在MRC中并发队列创造出来的需要 release 操作,而全局队列只有一个不需要.
  • 一般在开发过程中我们使用全局队列

2.同步和异步:

同步异步

同步:只能在当前线程中执行任务,不具备开启新线程的能力 异步:可以在新的线程中执行任务,具备开启新线程的能力

同步执行任务: dispatch_sync(队列,任务) 异步执行: dispatch_async(队列,任务)

队列和执行方式组合的效果:

1) 串行队列同步执行,既在当前线程中顺序执行 2) 串行队列异步执行,开辟一条新的线程,在该线程中顺序执行 3) 并行队列同步执行,不开辟线程,在当前线程中顺序执行 4) 并行队列异步执行,开辟多个新的线程,并且线程会重用,无序执行 5) 主队列异步执行,不开辟新的线程,顺序执行 6) 主队列同步执行,会造成死锁(’主线程’和’主队列’相互等待,卡住主线程)

死锁原因

队列引起的循环等待

同步/异步和串行/并发

  • dispatch_sync(serial_queue,^{//任务});
  • dispatch_async(serial_queue,^{//任务});
  • dispatch_sync(concurrent_queue,^{//任务});
  • dispatch_async(concurrent_queue,^{//任务});

首先明确几个概念

  • 队列:队列分为串行和并行。串行队列按照A、B、C、D的顺序添加四个任务,这四个任务按照顺序执行,结束顺序也肯定是A、B、C、D,而并行队列同时执行这四个任务,完成的顺序因此也是随机的。
  • 异步执行(async)和同步执行(sync):使用dispatch_async调用一个block,这个block会被放到指定的queue_1队列尾等待执行,至于这个block是被并行还是串行执行,只和dispatch_async中的指定的queue_1有关,但是dispatch_async会马上返回。使用dispatch_sync同样也是把block放到指定的queue_2上执行,但是会等待这个block执行完毕后才返回,这期间会阻塞当前运行调用dispatch_async或dispatch_sync代码的queue(通常为main_queue)直到sync函数返回。

以打电话给查号台为例:

  • 同步:打电话给查号台,问某个地方的电话号码,接线员会告诉你稍等,然后为你查号,此时你的电话没有挂断,其他的电话也不能打进来,等到接线员查找到了你要找的电话号,告诉你后,才将电话挂断
  • 异步:打电话给查号台,问某个地方的电话号码,接线员知道了你的请求后,会立刻挂断电话,此时其他的电话可以打进来。然后开始为你查号。等到查找到了你要找的电话号,会再打电话通知你。

所以任何情况下调用 dispatch_sync,都会在当前线程上执行该任务,而不继续走下去,直到任务执行完成

同步串行

1.

-(void)viewDidLoad{
    NSLog(@"执行任务1");
        dispatch_queue_t queue = dispatch_get_main_queue();
        dispatch_sync(queue, ^{
            NSLog(@"执行任务2");
        });

        NSLog(@"执行任务3");
//死锁
}

在主队列上提交了 viewDidLoadGCD Block的任务,无论任务中哪一个,最终都要提交到主线程中处理.先分派viewDidLoad到主线程,由于队列FIFO,viewDidLoad的调用结束又要等待Block的调用结束,Block又在等待viewDidLoad

只要是同步方式提交任务,无论是提交到并发队列还是串行队列,最终都是在当前线程执行

分析:

  • 1、主线程中任务执行:任务1、sync、任务3、
  • 2、主队列:viewDidLoad、任务2、

其中在主队列viewDidLoad里面的任务3执行结束才会执行任务2;而主线程中是执行完sync才会执行任务3。也就是任务2等待任务3执行,任务3再也等待任务2执行,造成死锁

2.

-(void)viewDidLoad{
    dispatch_sync(serialQueue,^{
        [self doSomething];
    });
//没问题
}

viewDidLoad添加到主队列上,提交到主线程上执行.viewDidLoad执行到某个时段时候,同步提交一个任务到一个串行队列上面,由于是同步提交任务,意味着要在当前线程执行,所以串行队列提交的任务也是在主线程上面执行,串行队列任务在主线程上执行完之后,再继续执行viewDidLoad后面的任务

3.

-(void)viewDidLoad{
    NSLog(@"1");
    dispatch_sync(global_queue,^{
        NSLog(@"2");
        dispatch_sync(global_queue,^{
            NSLog(@"3");
        });
        NSLog(@"4");
    });
    NSLog(@"5");
}

//12345

异步串行

1.

-(void)viewDidLoad{
    dispatch_async(dispatch_get_main_queue(),^{
        [self doSomething];
    });
}

异步并发

1.

dispatch_async(global_queue,^{
    NSLog(@"1");
    [self performSelector:@selector(printLog) withObject:nil afterDelay0];
    NSLog(@"3");
});

-(void)printLog{
    NSLog(@"2");
}
//13

因为子线程不会主动创建runloopperformSelector:withObject:afterDelay,即使延时0s,也是要创建相应添加到runloop逻辑,如果没有runloop是不会添加到上面,所以不会触发.(创建runloop后也需要Run)

dispatch_barrier_async()

怎么利用GCD实现多读单写?

  • 读者读者并发
  • 读者写者互斥
  • 写者写者互斥

多读单写处理

多读单写方案

dispatch_barrier_async(concurrent_queue,^{//写操作});
//同步读取指定数据
-(id)objectForKey:(NSString*)key{
    __block id obj;
    dispatch_sync(concurrent_queue,^{
        obj = xxxx;
    });
}

//写
-(void)setObject:(id)obj forKey:(NSString*)key{
    //异步栅栏调用设置数据
    dispatch_barrier_async(concurrent_queue,^{
        xxxxx;
    });
}

NSOperation

1. NSOprationQueue 与 GCD 的区别与选用

  1. GCD是底层的C语言构成的API,而NSOperationQueue及相关对象是Objc的对象。在GCD中,在队列中执行的是由block构成的任务,这是一个轻量级的数据结构;而Operation作为一个对象,为我们提供了更多的选择;

  2. 在NSOperationQueue中,我们可以随时取消已经设定要准备执行的任务(当然,已经开始的任务就无法阻止了),而GCD没法停止已经加入queue的block(其实是有的,但需要许多复杂的代码);

  3. GCD 只支持FIFO 的队列,而NSOperationQueue可以调整队列的执行顺序(通过调整权重)。NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。

  1. GCD优点:GCD主要与block结合使用。

引申:

使用NSOperation和NSOperationQueue的优点:

  1. 可以取消操作:在运行任务前,可以在NSOperation对象调用cancel方法,标明此任务不需要执行。但是GCD队列是无法取消的,因为它遵循“安排好之后就不管了(fire and forget)”的原则。
  2. 可以指定操作间的依赖关系:例如从服务器下载并处理文件的动作可以用操作来表示。而在处理其他文件之前必须先下载“清单文件”。而后续的下载工作,都要依赖于先下载的清单文件这一操作。
  3. 监控NSOperation对象的属性:可以通过KVO来监听NSOperation的属性:可以通过isCancelled属性来判断任务是否已取消;通过isFinished属性来判断任务是否已经完成。
  4. 可以指定操作的优先级:操作的优先级表示此操作与队列中其他操作之间的优先关系,我们可以指定它

状态控制

  • 如果只重写main方法,底层控制变更任务执行完成状态,以及任务退出
  • 如果重写了start方法,自行控制状态(什么时候是isExecuting,isFinish状态等等)

系统怎么移除一个 isFinished==YES 的NSOperation的 通过KVO

results matching ""

    No results matching ""