zoukankan      html  css  js  c++  java
  • iOS-----使用GCD实现多线程

    使用GCD实现多线程

    GCD的两个核心概念如下:

    队列

    队列负责管理开发者提交的任务,GCD队列始终以FIFO(先进先出)的方式来处理任务---但

    由于任务的执行时间并不相同,因此先处理的任务并一定先结束。队列既可是串行队列,也可是并发队列则可同时处理多个任务,因此将会有多个任务并发执行。

    队列底层会维护一个线程池来处理用户提交的任务,线程池的作用就是执行队列管理的任务。串行队列底层的线程池只要维护一个线程即可,并发队列的底层则需要维护多个线程。

    任务

    任务就是用户提交给队列的工作单元,这些任务将会提交给队列底层维护的线程池执行,因此这些任务会以多线程的方式执行。

    使用GCD只要遵守两个步骤即可

    1.

    创建队列

    2.

    将任务提交给队列

         

    创建队列

    GCD的队列可分为两种

    串行队列

    串行队列底层的线程只要一个线程,因此只提供一个线程用来执行任务,所以后一个任必须等到前一个任务执行结束才能开始执行

    并发队列

    线程池提供多个线程来执行任务,所以可以按FIFO(先进先出)的顺序并发启动、执行多个并发任务。

    函数

     

    涉及一个dispatch_queue_t,这种类型就代表一个队列。

    程序可以创建如下几种队列

    1. 获取系统默认的全局并发队列

    获取系统默认的全局并发队列可通过如下代码完成:

    dispatch_queue_t  queue = dispatch_get_global_queue(

    DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)

    1. 获取系统主线程关联的串行队列

    获取系统主线程关联的串行队列可通过如下代码完成:

    dispatch_queue_t  queue = dispatch_get_main_queue();

    如果将任务提交给主线程关联的串行队列,那么就相当于直接在程序主线程中去执行该任务。

    1. 创建串行队列

    创建串行队列可通过如下代码完成:

    dispatch_queue_t  queue = dispatch_queue_create(“LCiOS.queue”,

    DISPATCH_QUEUE_SERIAL);

       如果将多个任务提交给串行队列,多个任务只能按顺序执行,必须等前一个任务完成后,才能开始执行后一个任务。

    1. 创建并发队列

    创建并发队列可通过如下代码完成:

    dispatch_queue_t queue = dispacth_queue_create(“LCiOS.queue”

    , DISPATCH_QUEUE_CONCURRENT);

    如果将多个任务提交给并发队列,并发队列可以按FIFO(先进先出)的顺序启动多个并发执行的任务,由于任务的耗时长短并不相同,因此后提交的任务完全可能先完成。

    得到队列之后,接下来就可以将任务提交给队列,并由队列底层管理的线程池来执行这些任务。

         

    异步提交任务

    iOS提供了如下函数来向队列提交任务。下面这些函数很多都有两个版本:一个接收代码块作为参数的版本,

    一个接收函数作为参数的版本----其中接收函数作为参数的函数名最后多了_f后缀,而且会多一个参数,用于向函数传入应用程序定义的上下文。

      1 ViewController.m
      2 
      3 @implementation ViewController
      4 
      5 // 定义2个队列
      6 
      7 dispatch_queue_t  serialQueue;
      8 
      9 dispatch_queue_t  concurrentQueue;
     10 
     11 - (void)viewDidLoad
     12 
     13 {
     14 
     15    [super viewDidLoad];
     16 
     17    // 创建串行队列
     18 
     19 serialQueue = dispatch_queue_create(“LCiOS.queue”,DISPATCH_QUEUE_SERIAL);
     20 
     21 // 创建并发队列
     22 
     23 concurrentQueue = dispatch_queue_create(“LCiOS.queue”,DISPATCH_QUEUE_CONCURRENT);
     24 
     25 }
     26 
     27 - (IBAction)serial:(id)sender
     28 
     29 {
     30 
     31   // 依次将两个代码块提交给串行队列
     32 
     33   // 必须等到第1个代码块完成后,才能执行第2个代码块
     34 
     35   dispatch_async(serialQueue,  ^(void)
     36 
     37 {
     38 
     39    for(int i = 0; i < 100; i++)
     40 
     41 {
     42 
     43    NSLog(@”%@====%d”, [NSThread  currentThread], i);
     44 
     45 }
     46 
     47 });
     48 
     49 dispatch_async(serialQueue,  ^(void)
     50 
     51 {
     52 
     53    for(int i = 0; i < 100; i++)
     54 
     55 {
     56 
     57    NSLog(@”%@-----%d”, [NSThread  currentThread], i);
     58 
     59 }
     60 
     61 });
     62 
     63 }
     64 
     65 - (IBAction)concurrent:(id)sender
     66 
     67 {
     68 
     69   // 依次将两个代码块提交给并发队列
     70 
     71   // 两个代码块可以并发执行
     72 
     73   dispatch_async(concurrentQueue,  ^(void)
     74 
     75 {
     76 
     77    for(int i = 0; i < 100; i ++)
     78 
     79 {
     80 
     81    NSLog(@”%@====%d”, [NSThread  currentThread] ,  i);
     82 
     83 }
     84 
     85 });
     86 
     87 dispatch_async(concurrentQueue,  ^(void)
     88 
     89 {
     90 
     91    for(int i = 0; i < 100; i ++)
     92 
     93 {
     94 
     95    NSLog(@”%@-----%d”, [NSThread  currentThread] ,  i);
     96 
     97 }
     98 
     99 });
    100 
    101 }
    102 
    103 @end

        上面程序中的两行粗体字代码创建了两个队列,其中第1个队列是串行队列,第2个队列是并发队列。接下来程序实现了serial:和concurrent:两个事件处理方法,在serial:方法中使用dispatch_async()函数向串行队列以异步方式提交两个代码块,在concurrent:方法中使用dispatch_async()函数向并发队列以异步方式提交两个代码块。

      编译、运行该程序,如果用户单击的按钮控制程序向串行队列提交两个代码块,将可以在控制台看到如下图所示的输出。

    如果

    如果用户单击的按钮控制程序向并发队列提交两个代码块,将可以在控制台看到如下图所示的输出

    使用G

    CD下载图片

       

     1 ViewController.m
     2 
     3 @implementation ViewController
     4 
     5 - (void)viewDidLoad
     6 
     7 {
     8 
     9    [super viewDidLoad];
    10 
    11 }
    12 
    13 - (IBAction)downImag:(id)sender
    14 
    15 {
    16 
    17 // 将代码块提交给系统的全局并发队列
    18 
    19 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH , 0) , ^(void){
    20 
    21     NSString* url = @” http://images.cnblogs.com/cnblogs_com/congli0220/752817/o_%e4%b8%8b%e8%bd%bd.jpg”;
    22 
    23       // 从网络获取数据
    24 
    25        NSData *data = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:url]];
    26 
    27      // 将网络数据初始化为UIImage对象
    28 
    29     UIImage *image = [[UIImage alloc] initWithData:data];
    30 
    31     if(image != nil)
    32 
    33     {
    34 
    35       // 将代码块提交给主线程关联的队列,该代码将会由主线程完成
    36 
    37       dispatch_async(dispatch_get_main_queue(), ^{
    38 
    39        self.iv.image = image;
    40 
    41 });// 1
    42 
    43 }
    44 
    45 else
    46 
    47 {
    48 
    49   NSLog(@”---下载图片出现错误---”);
    50 
    51 }
    52 
    53 });
    54 
    55 }
    56 
    57 @end

    说明

     该程序中的代码会将代码块提交给系统默认的全局并发队列,该代码块就会负责从网络下载图片。由于该代码块同样会在多线程中执行,因此程序的“1”号代码处再次使用了dispatch_async()函数将更新界面上UI控件的代码交给主线程执行.

    同步提交任务

    dispatch_sync()函数则会以同步方式提交代码块,该函数必须等到代码块执行结束才会返回.如果程序使用该函数先后提交了两个代码块(即使提交给并发队列),也必须等第1个任务完成后才会开始执行第2个任务.

    例如如下示例的视图控制器类的实现部分代码

     1 ViewController.m
     2 
     3 @implementation ViewController
     4 
     5 - (void)viewDidLoad
     6 
     7 {
     8 
     9    [super viewDidLoad];
    10 
    11 }
    12 
    13 - (IBAction)clicked:(id)sender
    14 
    15 {
    16 
    17   // 以同步方式先后提交两个代码块
    18 
    19   dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
    20 
    21              forint i = 0 ; i < 100; i++22 
    23              {
    24 
    25                  NSLog(@”%@====%d”, [NSThread currentThread] , i);
    26 
    27     [NSThread sleepForTimeInterval:0.1];
    28 
    29 }
    30 
    31 });
    32 
    33 // 必须等第1次提交的代码块执行完成后,dispatch_sync()函数才会返回
    34 
    35 // 程序才会执行到这里,才能提交第2个代码块
    36 
    37 dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0  ), ^(void){
    38 
    39      for(int i = 0 ; i < 100; i ++)
    40 
    41      {
    42 
    43        NSLog(@”%@------%d”, [NSThread  currentThread] , i);
    44 
    45        [NSThread  sleepForTimeInterval:0.1];
    46 
    47 }
    48 
    49 });
    50 
    51 }
    52 
    53 @end

    说明

       上面程序使用dispatch_sync()函数以同步方式提交代码块,该函数必须等到所提交的代码块执行完成后才会返回,因此该函数虽然启动另外的线程来执行代码块,但它依然会阻塞主线程.

    上面程序先后两次使用dispatch_sync()函数来提交代码块,因此程序必须等到第1次使用dispatch_sync()函数提交的代码块执行完成后,该函数才会返回,程序才会执行第2次提交.

    只有等到两次提交的代码块都执行完成后,clicked:事件处理方法才能返回----表明事件响应执行完成.如果用户单击激发该事件的按钮,该按钮将会一直处于高亮状态,直到两个代码块执行完成

    多次执行的任务

      dispatch_apply()函数将控制提交的代码块重复执行多次,如果该代码块被提交给并发队列,系统可以使用多个线程并发执行同一个代码块.

       下面示例程序在界面上包含了一个按钮,当用户单击该按钮时将会使用dispatch_apply()函数将代码块提交给并发队列,并控制该代码块执行多次.

       下面是该示例的视图控制器类的实现部分代码.

     1 ViewController.m
     2 
     3 @implementation ViewController
     4 
     5 - (void)viewDidLoad
     6 
     7 {
     8 
     9    [super viewDidLoad];
    10 
    11 }
    12 
    13 - (IBAction)clicked:(id)sender
    14 
    15 {
    16 
    17    // 控制代码执行5次
    18 
    19    dispatch_apply(5 , dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    20 
    21 // time形参代表当前正在执行第几次
    22 
    23 ,^(size_t time)
    24 
    25 {
    26 
    27    NSLog(@===执行【%lu】次===%@”, time , [NSThread currentThread]);
    28 
    29 });
    30 
    31 }
    32 
    33 @end

    说明

    上面程序中的粗体字代码使用dispatch_apply()函数控制提交的代码块执行5次,该函数所需的代码块略有不同,该代码块可以带一个参数,该参数代表当前正在执行第几次.

    只执行一次的任务

    dispatch_once()函数将控制提交的代码块在整个应用的生命周期内最多只执行一次----只有第1次提交该代码块时,

    该代码块才会获得执行的机会.而且dispatch_once()函数无须传入队列,这意味着系统将直接用主线程执行该函数提交的代码块.

    dispatch_once()函数执行时需要传入一个dispatch_once_t类型(本质就是long型整数)的指针(即predicate参数),

    该指针变量用于判断该代码块是否已经执行过.

    下面示例程序在界面上包含了一个按钮,当用户单击该按钮时激发的事件处理方法将会使用dispatch_once()函数提交代码块,

    将代码块提交给主线程执行,因此该代码块可能阻塞主线程.但当用户再次单击该按钮时,dispatch_once()函数提交的代码块不会再执行.

    代码片段

     1 ViewController.m
     2 
     3 @implementation ViewController
     4 
     5 - (void)viewDidLoad
     6 
     7 {
     8 
     9    [super viewDidLoad];
    10 
    11 }
    12 
    13 - (IBAction)clicked:(id)sender
    14 
    15 {
    16 
    17   static dispatch_once_t onceToken;
    18 
    19   dispatch_once(&onceToken, ^{
    20 
    21 NSLog(@”==执行代码块==”);
    22 
    23 // 线程暂停3秒
    24 
    25 [NSThread sleepForTimeInterval:3];
    26 
    27 });
    28 
    29 }
    30 
    31 @end

     

     
  • 相关阅读:
    既使用maven编译,又使用lib下的Jar包
    Mac idea 快捷键
    spring-boot集成Springfox-Swagger2
    java 反射获取类的静态属性值
    springmvc防止重复提交拦截器
    反射调用 java bean的set和get方法
    JSONObject 自定义过滤配置
    maven自定义脚手架(快速生成项目)
    Jackson序列化实例
    EntityManager的使用
  • 原文地址:https://www.cnblogs.com/congli0220/p/5016166.html
Copyright © 2011-2022 走看看