IOS多线程[总结]

概念

并发队列:1.可以让多个任务并发(同时)执行(自动开启多个线程同时执行任务)
2.并发功能只有在异步(dispatch_async)函数下才有效
串行队列: 让任务一个接着一个的执行 (一个任务执行完毕再执行下一个任务)
### 队列
自己创建的并发队列: 同步函数(没有开新线程能力) 顺序执行 没有开启新线程
自己创建的并发队列(效果等同于全局队列): 异步函数(有开新线程能力) 并发执行 有开启新线程(多条,系统决定)

自己创建的串行队列: 同步函数(没有开新线程能力)   顺序执行   没有开启新线程
自己创建的串行队列: 异步函数(有开新线程能力)   顺序执行   有开启新线程(队列只开启一条子线程[还有一条是所在的线程,新开的线程和所在的线程是并行的],顺序执行)
注意:1.串行队列里边的同步任务和异步任务,不可嵌套同步任务(死锁),可以嵌套异步任务
     2.在某一时间间隔内,有两个或者两个以上的任务一起执行(本质还是串行的,只不过按照时间片轮转的方式交替执行)。
主队列:同步函数(死锁,解决方法,放到子线程去执行)
主队列:异步函数(有开新线程能力) 顺序执行   没有开启新线程(主队列所有的任务确实是在主线程执行的, 虽然是异步函数, 但也不会开启线程)

理解:

- 串行(一个接一个)和并行( 并发)队列没有开新线程的能力,串行队列中异步和同步只允许开启一条线程(异步开启任务,同步不开启在当前线程),并行队列可允许开启多条。

1.主队列)中一个同步会死锁
2.在子线程中主队列,同步嵌套会死锁
3.(同上)串行队列里边同步嵌套同步会死锁,并行队列则不会
- 自定义串行,并行队列,相当于在当前线程(分配了一个子线程)(非主只线程)中执行
屏幕快照 2017-06-27 下午5.13.10.png

###常用:

  • 线程间通信 子线程中返回主线程
  • 延迟函数 dispatch_after
  • 一次性执行 dispatch_once
  • 栅栏函数 dispatch_barrier_async(在异步函数中控制任务执行的顺序, 只有当栅栏函数执行完毕之后才会执行后面的任务.)
  • 快速迭代 dispatch_apply (GCD 快速迭代的是开启了子线程去执行的,而且主线程也参与了, 由于不是一个线程, 所以迭代也不是按顺序的. 明显快于 for 循环遍历)

  • 队列组 dispatch_group_t 当执行队列组通知模块时能保证放进队列组里的任务全部执行完毕
    同步函数和并发队列

    NSOperation

    NSOperation
    1.不用管串行、并行、同步、异步这些名词,用maxConcurrentOperationCount 最大并发数来控制
    2.添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖

    NSOperation

    BOOL executing; //判断任务是否正在执行
    BOOL finished; //判断任务是否完成
    void (^completionBlock)(void); //用来设置完成后需要执行的操作
    - (void)cancel; //取消任务
    - (void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕
    NSOperationQueue

    NSUInteger operationCount; //获取队列的任务数
    - (void)cancelAllOperations; //取消队列中所有的任务
    - (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的所有任务执行完毕
    [queue setSuspended:YES]; // 暂停queue
    [queue setSuspended:NO]; // 继续queue


    ###互斥锁
    1.@synchronized(self) {
        //需要执行的代码块
    }
   2. //GCD
    //需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
    dispatch_sync(queue, ^{
        NSInteger ticket = lastTicket;
        [NSThread sleepForTimeInterval:0.1];
        NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
        ticket -= 1;
        lastTicket = ticket;
    });
   3. //NSOperation & NSOperationQueue
    //重点:1. 全局的 NSOperationQueue, 所有的操作添加到同一个queue中
    //       2. 设置 queue 的 maxConcurrentOperationCount 为 1
    //       3. 如果后续操作需要Block中的结果,就需要调用每个操作的waitUntilFinished,阻塞当前线程,一直等到当前操作完成,才允许执行后面的。waitUntilFinished 要在添加到队列之后!
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSInteger ticket = lastTicket;
        [NSThread sleepForTimeInterval:1];
        NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
        ticket -= 1;
        lastTicket = ticket;
    }];
    [queue addOperation:operation];
    [operation waitUntilFinished];
    //后续要做的事

原文:
http://www.jianshu.com/p/905796f91f21
http://www.jianshu.com/p/b244476ce561?nomobile=yes
http://www.jianshu.com/p/d48d425df716?utm_source=desktop&utm_medium=timeline
1.队列主要包含三种:主线程队列、并行队列、串行队列
2.任务主要包含两种:异步任务和同步任务
多线程执行过程就是把任务放在队列中去执行的过程

一.1.主线程队列(串行)

    dispatch_queue_t  queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{

    });
  或者
    dispatch_async(dispatch_get_main_queue(), ^{

    });

主线程队列 内部执行任务是串行的同步操作,按顺序执行

2.私有串行队列(串行)

// 创建一个用户串行队列 dispatch_queue_create("com.gcd", NULL),"com.gcd":队列名是一个C字符串,没有特别的要求,Apple建议用倒装的标识符来表示  
//串行队列内部也是串行操作
    dispatch_async( dispatch_queue_create("com.gcd", NULL), ^{
          //任务
      });
     // 我们自己创建的队列,我们需要自己销毁(非arc  需要销毁)
      //dispatch_release(queue);

实例


-(void)tandemPrivateQueue{
    //自定义私有串行队列
    dispatch_queue_t queue=dispatch_queue_create("com.privateQueue", NULL);

    //异步任务1加入队列中
    dispatch_async(queue, ^{
        NSLog(@"私有队列任务1");
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"私有队列任务1_i:%ld",i);
        }     
    });
    //异步任务2加入队列中
    dispatch_async(queue, ^{
        NSLog(@"私有队列任务2");
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"私有队列任务2_i:%ld",i);
        }

    });

}

私有队列中任务是串行,先任务一,在任务二

3.全局队列 并行队列

如果是 同步(sync) 操作,它会阻塞当前线程并等待 Block 中的任务执行完毕,然后当前线程才会继续往下运行。
如果是 异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。

   /*
    // 并行队列(全局)不需要我们创建,通过dispatch_get_global_queue()方法获得
    // 三个可用队列
    // 第一个参数是选取按个全局队列,一般采用DEFAULT,默认优先级队列
    // 第二个参数是保留标志,目前的版本没有任何用处(不代表以后版本),直接设置为0就可以了
    // DISPATCH_QUEUE_PRIORITY_HIGH
    //DISPATCH_QUEUE_PRIORITY_DEFAULT
    // DISPATCH_QUEUE_PRIORITY_LOW
    */
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

实例

###任务1和任务2同时进行
-(void)parallelGlobalQueue{
    //全局并行队列
    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_async(globalQueue, ^{
        NSLog(@"全局队列任务1");
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"全局队列任务1_i:%ld",i);
            [NSThread sleepForTimeInterval:0.5];
        }
    });
    dispatch_async(globalQueue, ^{
        NSLog(@"全局队列任务2");
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"全局队列任务2_i:%ld",i);
            [NSThread sleepForTimeInterval:0.5];
        }
    });
}
###任务1和任务2串行进行
-(void)parallelGlobalQueue{
    //并行全局队列
    dispatch_queue_t globalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //添加一个同步任务1
     dispatch_sync(globalQueue, ^{
        NSLog(@"全局队列任务1");
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"全局队列任务1_i:%ld",i);
        }
    });
    //添加一个同步任务2
     dispatch_sync(globalQueue, ^{
        NSLog(@"全局队列任务2");
        for (NSInteger i = 0; i < 10; i++) {
            NSLog(@"全局队列任务2_i:%ld",i);
        }
    });
}

二 .1.任务

//同步任务:
  dispatch_sync(, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });

//异步任务:
  dispatch_async(, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });

三,对UI的影响

1.不论在串行队列还是并行队列只要有同步任务的出现都会执行完block中代码才会接着往下进行。
2.队列只装载任务并管理任务的顺序,是同时来或一个一个来,有没有影响由队列中任务来决定

注意点

Dispatch Queues的生成可以有这几种方式:  

1. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.serial", DISPATCH_QUEUE_SERIAL); //生成一个串行队列,队列中的block按照先进先出(FIFO)的顺序去执行,实际上为单线程执行。第一个参数是队列的名称,在调试程序时会非常有用,所有尽量不要重名了。  

2. dispatch_queue_t queue = dispatch_queue_create("com.dispatch.concurrent", DISPATCH_QUEUE_CONCURRENT); //生成一个并发执行队列,block被分发到多个线程去执行  

3. dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); //获得程序进程缺省产生的并发队列,可设定优先级来选择高、中、低三个优先级队列。由于是系统默认生成的,所以无法调用dispatch_resume()和dispatch_suspend()来控制执行继续或中断。需要注意的是,三个队列不代表三个线程,可能会有更多的线程。并发队列可以根据实际情况来自动产生合理的线程数,也可理解为dispatch队列实现了一个线程池的管理,对于程序逻辑是透明的。  

官网文档解释说共有三个并发队列,但实际还有一个更低优先级的队列,设置优先级为DISPATCH_QUEUE_PRIORITY_BACKGROUND。Xcode调试时可以观察到正在使用的各个dispatch队列。  

4. dispatch_queue_t queue = dispatch_get_main_queue(); //获得主线程的dispatch队列,实际是一个串行队列。同样无法控制主线程dispatch队列的执行继续或中断。  

接下来我们可以使用dispatch_asyncdispatch_sync函数来加载需要运行的block。  
  //串行队列同步嵌套会死锁,并行队列同步嵌套,会同步执行,异步嵌套的没有顺序
dispatch_async(queue, ^{  

  //block具体代码  

}); //异步执行block,函数立即返回  

dispatch_sync(queue, ^{  

  //block具体代码  

}); //同步执行block,函数不返回,一直等到block执行完毕。编译器会根据实际情况优化代码,所以有时候你会发现block其实还在当前线程上执行,并没用产生新线程。  

实际编程经验告诉我们,尽可能避免使用dispatch_sync,嵌套使用时还容易引起程序死锁。  

如果queue1是一个串行队列的话,这段代码立即产生死锁:  

   dispatch_sync(queue1, ^{  

      dispatch_sync(queue1, ^{  

    ......  

  });  

  ......  

 });   

那实际运用中,一般可以用dispatch这样来写,常见的网络请求数据多线程执行模型:  

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{  

  //子线程中开始网络请求数据  

  //更新数据模型  

  dispatch_sync(dispatch_get_main_queue(), ^{  

    //在主线程中更新UI代码  

  });  

});  

程序的后台运行和UI更新代码紧凑,代码逻辑一目了然。  



dispatch队列是线程安全的,可以利用串行队列实现锁的功能。比如多线程写同一数据库,需要保持写入的顺序和每次写入的完整性,简单地利用串行队列即可实现:  

dispatch_queue_t queue1 = dispatch_queue_create("com.dispatch.writedb", DISPATCH_QUEUE_SERIAL);  

- (void)writeDB:(NSData *)data  

{  

  dispatch_async(queue1, ^{  

    //write database  

  });  

}   

下一次调用writeDB:必须等到上次调用完成后才能进行,保证writeDB:方法是线程安全的。   



dispatch队列还实现其它一些常用函数,包括:  

void dispatch_apply(size_t iterations, dispatch_queue_t queue, void (^block)(size_t)); //重复执行block,需要注意的是这个方法是同步返回,也就是说等到所有block执行完毕才返回,如需异步返回则嵌套在dispatch_async中来使用。多个block的运行是否并发或串行执行也依赖queue的是否并发或串行。  

void dispatch_barrier_async(dispatch_queue_t queue, dispatch_block_t block); //这个函数可以设置同步执行的block,它会等到在它加入队列之前的block执行完毕后,才开始执行。在它之后加入队列的block,则等到这个block执行完毕后才开始执行。  

void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block); //同上,除了它是同步返回函数  

void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block); //延迟执行block  

最后再来看看dispatch队列的一个很有特色的函数:  

void dispatch_set_target_queue(dispatch_object_t object, dispatch_queue_t queue);  

它会把需要执行的任务对象指定到不同的队列中去处理,这个任务对象可以是dispatch队列,也可以是dispatch源(以后博文会介绍)。而且这个过程可以是动态的,可以实现队列的动态调度管理等等。比如说有两个队列dispatchA和dispatchB,这时把dispatchA指派到dispatchB:  

dispatch_set_target_queue(dispatchA, dispatchB);  

那么dispatchA上还未运行的block会在dispatchB上运行。这时如果暂停dispatchA运行:  

dispatch_suspend(dispatchA);  

则只会暂停dispatchA上原来的block的执行,dispatchB的block则不受影响。而如果暂停dispatchB的运行,则会暂停dispatchA的运行。  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值