iOS知识梳理之多线程GCD

1.线程是什么,多线程是什么

当我写出代码,代码通过编译器转换为的CPU命令列(二进制代码).

int main{
  id o = [[MyObject alloc] init];
  [o execBlock];
  return 0;
}
OOOOOlac:   b590 push (r4, r7, lr)
OOOOOlae:   f240019c movw rl, :lowerl6:0x260-0xlc0+0xfffffffc
000001b2:   afOl add    r7, sp, #4
000001b4:   f2c00100 movt rl, :upperl6:0x260-0xlc0+0xfffffffc 
000001b8:   f24010be movw r0, :lowerl6:0x384-0xlc2+0xfffffffc 
OOOOOlbc:   f2c00000 movt rO, :upperl6:0x384-0xlc2+0xfffffffc
OOOOOlcO:   4479 add    rl, pc
000001c2:   4478 add    r0, pc
000001c4:   6809 ldr    rl, [rl, #0】
000001c6:   6800 ldr    rO, [rO, #0]
000001c8:   f7ffefla blx    _obj c_msgSend
OOOOOlcc:   f2400180 movw rlr :lowerl6:0x258-0xld4+0xfffffffc
OOOOOldO:   f2c00100 movt rl, :upperl6:0x258-0xld4+0xfffffffc
000001d4:   4479 add    rl, pc
000001d6:   6809 ldr    rl, (rl, #0]
000001d8: f7ffefl2 blx _obj c_msgSend OOOOOldc: 4604 mov r4, rO
OOOOOlde:   f240007a movw r0, :lowerl6:0x264-0xle6+0xfffffffc
000001e2:   f2c00000 movt rO, :upperl6:0x264-0xle6+0xfffffffc
000001e6:   4478 add    rO, pc
000001e8: 6801 Idr rl, [rO, *0J 
OOOOOlea: 4620 mov rO, r4
OOOOOlec: f7ffef08 blx  _objc_msgSend 
OOOOOlfO:   4620 mov    rO, r4 
000001f2:   f7ffef06 blx    _objc_release 
000001fe:   2000 movs rO, #0 
000001f8:   bd90 pop    {r4, r7, pc)

以上的命令会在CPU上一个一个的依次的执行,不会出现两个命令在一个CPU上一起执行的场景

1.1 最初

定义1.0:「一个CPU执行的CPU命令列为一条无分叉路径----线程」

1.2 CPU相关科技发展,一个CPU有了多个核,真的多线程

定义2.0:「一个CPU核执行的CPU命令列为一条无分叉路径----线程」
一个多核CPU,每个核跑一个线程,自然有了多线程

1.3 CPU相关科技再发展,一个单核的CPU,也可以看起来像'多线程'

一个单核的CPU切割时间片段,
假设单位时间片段为1/60秒,
被开启线程有1号线程,2号线程,3号线程,也就是说在一秒内:
第一个1/60秒在跑1号线程,
第二个1/60秒在跑2号线程,
第三个1/60秒在跑3号线程,
.....
如此循环往复也有了有了多线程

定义3.0:「一个CPU核同一时间内执行的CPU命令列为一条无分叉路径----线程」

1.4 上下文切换

换跑道:上下文切换
CPU核由路径1切换到路径2时,会将路径1在CPU寄存器上的信息全部清除.
问题来了,CPU再跑路径1时,怎么拿回这些数据呢?
老路径在CPU的寄存器的信息会保存到专有的内存块上.
新路径的在内存上的数据会被加载到CPU的寄存器上,
如此循环往复.
这就是传说中的----上下文切换

所以,使用太多线程会导致消耗大量内存

GCD(Grand Central Dispatch)属于系统级的线程管理

2 为什么要用多线程

app的主线程是用于更新UI,处理触摸屏幕的.

我们想象一个app内只有一个主线程,处理任务绝对按照1->2->3->4的来,
1:登录
2:网络请求
3:网络回调
4:登录成功跳转
因为只有一个主线程,step2+step3时,app无法更新UI(就连给个loading也做不了),出现假死.

而有了多线程
1:登录
2:网络请求 //次线程
3:网络回调 //次线程
4:登录成功跳转
step2+step3在次线程中做,主线程给出loading
网络回调后再通知主线程更新UI

多线程:卡谁都别卡主线程,因为主线程是要保证app如丝般润滑的.

多线程用自己的多维性保证了主线程的绝对通畅,同时多线程也因为自己的多维无序性带来了许多问题:
多线程的共同参与一份数据的读写一>大家都乱来一>数据竞争
线程等线程一>大家都不动手一>死锁

3.队列

队列分为串行队列和并行队列,队列全部符合FIFO的原则(先入先出)
串行队列内只有一个线程完成加到队列内的任务
并行队列内有多个线程完成加到队列内的任务

队列内的任务一定是按照先进先出的原则逐一开始的
串:[op1 start]->[op2 start]->[op3 start]->[op4 start]
并:[op1 start]->[op2 start]->[op3 start]->[op4 start]

只不过串行队列需要等待前一个任务完成开始下一个,而并行队列不需要等待.「因为串行队列只有一个线程在干活」
串:[op1 done]->[op2 done]->[op3 done]->[op4 done]
并:看心情_

  • Main dispatch queue
dispatch_queue_t queue = dispatch_get_main_queue();

Main dispatch queue是串行队列

  • global dispatch queue
dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

global dispatch queue是并行队列
global dispatch queue有四个优先级:

DISPATCH_QUEUE_PRIORITY_HIGH
DISPATCH_QUEUE_PRIORITY_DEFAULT
DISPATCH_QUEUE_PRIORITY_LOW
DISPATCH_QUEUE_PRIORITY_BACKGROUND
  • 自建串行队列
dispatch_queue_t serial_queue = dispatch_queue_create("com.pogong.www", DISPATCH_QUEUE_SERIAL);
  • 自建并行队列
dispatch_queue_t concurrent_queue = dispatch_queue_create("com.pogong.www", DISPATCH_QUEUE_CONCURRENT);

4.同步异步

  • dispatch_sync 同步
    同步意味阻塞当前线程,任务完成才返回
  • dispatch_async 异步
    异步不阻塞当前线程,任务不用完成就返回了

5.混合讨论

//外围线程
dispatch_async(dispatch_get_main_queue(), ^{
      //内部执行任务的线程
});

依照上面的格式:

外围线程执行模式执行队列内部执行任务的线程
主线程同步并行队列主线程
主线程异步并行队列多个次线程
主线程同步串行队列主线程
主线程异步串行队列单个次线程
次线程同步主队列主线程
次线程异步主队列主线程

执行队列为主队列:忽略同步异步,全在主线程执行.
可以看到,同步方法不一定在本线程,异步方法方法也不一定新开线程.

6.死锁

void (^myBlock)(void) = ^{
    NSLog(@"Block中 ");
};
dispatch_sync(dispatch_get_main_queue(), myBlock);

主线程正在执行dispatch_sync(),随后主队列中新增一个任务block.因为主队列是串行队列,所以主线程会在dispatch_sync()结束后再执行block,但是dispatch_sync是同步派发,要等block执行完才算是结束.在主队列中的两个任务互相等待,导致了死锁.

此处需要区分线程与队列:线程是跑任务的,队列是保存任务,并保证任务执行符合FIFO原则的.

dispatch_sync换成dispatch_async就可以避免死锁

void (^myBlock)(void) = ^{
     NSLog(@"Block中 ");
};
dispatch_async(dispatch_get_main_queue(), myBlock);

7. dispatch_group

7.1 dispatch_group_notify

多个任务在并行队列内无序的进行,需要在多个任务全部完成后立刻开启新任务,那么这时就是需要用到
dispatch_group_asyncdispatch_group_notify的组合

    dispatch_queue_t coucurrent_queue = dispatch_queue_create("com.pogong.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, coucurrent_queue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"group-01 - %@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, coucurrent_queue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"group-02 - %@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_notify(group, coucurrent_queue,^{
        NSLog(@"op finish,start new op");
    });

7.2 dispatch_group_wait

还是7.1同样的需求:当任务在并行队列内无序的进行,需要在多个任务结束后立刻开启新任务
也可以用dispatch_group_asyncdispatch_group_wait的组合来完成

    dispatch_queue_t coucurrent_queue = dispatch_queue_create("com.pogong.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_async(group, coucurrent_queue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"group-01 - %@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_async(group, coucurrent_queue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"group-02 - %@", [NSThread currentThread]);
        }
    });
    
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"op finish");

只是需要注意的是dispatch_group_wait会阻塞当前线程,或者说GCD的所有带_wait都会阻塞当前线程
dispatch_group_wait的第二参数为超时时间,DISPATCH_TIME_FOREVER代表一直等下去.

也可以将以上dispatch_group_wait代码换成:

int delayInSeconds = 20;
dispatch_time_t cheak_Time = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
long res = dispatch_group_wait(group, cheak_Time);
if (res == 0) {
NSLog(@"zc done");
}else{
NSLog(@"zc ing");
}

设置超时时间为20s,
1.group任务提前完成,提前返回
2.按指定时间返回

dispatch_group_wait的返回值为0代表group的任务全部完成,否则就代表任务还在进行中

7.3 dispatch_async+dispatch_group_enter+dispatch_group_leave

    dispatch_group_t group = dispatch_group_create();
    NSMutableArray * array = [NSMutableArray array];
    for (int i=0; i<5000; i++) {
        dispatch_group_enter(group);//enter和leave成对出现
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            
            NSLog(@"add in %@", [NSThread currentThread]);
            
            [array addObject:[NSNumber numberWithInt:i]];
            dispatch_group_leave(group);//enter和leave成对出现
            
        });
    }
    NSLog(@"before wait %@", [NSThread currentThread]);
    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);

还是7.1同样的需求:当任务在并行队列内无序的进行,需要在多个任务结束后立刻开启新任务
也可以用dispatch_async+dispatch_group_enter+dispatch_group_leaver的组合来完成

8.dispatch_after

double delayInSeconds = 5.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
    NSLog(@"zc after");
});

dispatch_after的用法如上方代码,就不多说了

9.信号量dispatch_semaphore

首先介绍一下信号量(semaphore)的概念。信号量是持有计数的信号,不过这么解释等于没解释。我们举个生活中的例子来看看。
假设有一个房子,它对应进程的概念,房子里的人就对应着线程。一个进程可以包括多个线程。这个房子(进程)有很多资源,比如花园、客厅等,是所有人(线程)共享的。
但是有些地方,比如卧室,最多只有两个人能进去睡觉。怎么办呢,在卧室门口挂上两把钥匙。进去的人(线程)拿着钥匙进去,没有钥匙就不能进去,出来的时候把钥匙放回门口。
这时候,门口的钥匙数量就称为信号量(Semaphore)。很明显,信号量为0时需要等待,信号量不为零时,减去1而且不等待。
此段描述完全摘自:bestswifer iOS多线程编程——GCD与NSOperation总结

9.1 多线程资源单一占用

    dispatch_queue_t q = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
    dispatch_semaphore_t sem = dispatch_semaphore_create(1);
    
    NSMutableArray * muArr = [NSMutableArray array];
    
    for (int i = 0; i< 1000; i++) {
        dispatch_async(q, ^{
            
            dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
            
            [muArr addObject:@(i)];
            NSLog(@"%d",i);
            
            dispatch_semaphore_signal(sem);
            
        });
    }

dispatch_semaphore_create,创建一个信号量为1的信号
dispatch_semaphore_wait,只有信号量大于0才能通过dispatch_semaphore_wait,通过的同时信号量减1
dispatch_semaphore_signal,信号量加1

9.2 超时显示

- (void)downloadFile
{
    _semaphore = dispatch_semaphore_create(0);
    NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"http://baobab.wdjcdn.com/14525705791193.mp4"] cachePolicy:1 timeoutInterval:30];
    [[self.session downloadTaskWithRequest:request] resume];
    
    double delayInSeconds = 5.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
    
    NSLog(@"zc wait");
    long res = dispatch_semaphore_wait(_semaphore, popTime);
    if (res) {
        NSLog(@"zc timed out");
    }else{
        NSLog(@"zc timed in");
    }
}

- (void)URLSession:(NSURLSession *)session downloadTask:(NSURLSessionDownloadTask *)downloadTask didFinishDownloadingToURL:(NSURL *)location
{
    dispatch_semaphore_signal(_semaphore);
    NSLog(@"zc done");
}

还是那句所有GCD带_wait函数都会阻塞当前线程

开始下载创建信号,
下载成功,增加信号量,

任务提前完成,提前返回
按指定时间返回
dispatch_semaphore_wait的返回值为0代表任务完成,否则就代表任务还在进行中

9.3 dispatch_semaphore_wait超时时间的理解

overTime不是调用dispatch_semaphore_wait后等待的时间,而是信号量创建后的时间

-(void)overTimeTest{
    dispatch_semaphore_t signal = dispatch_semaphore_create(0);
    double delayInSeconds = 5.0;
    dispatch_time_t overTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t) (delayInSeconds * NSEC_PER_SEC));
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"1 start wait");
        dispatch_semaphore_wait(signal, overTime);
        NSLog(@"需要线程同步的操作1 开始");
        dispatch_semaphore_signal(signal);
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(3);
        NSLog(@"2 start wait");
        dispatch_semaphore_wait(signal, overTime);
        NSLog(@"需要线程同步的操作2");
        dispatch_semaphore_signal(signal);
    });
}

以上两个block内的dispatch_semaphore_wait调用相差3秒,但执行dispatch_semaphore_wait却是同时的.

10.dispatch_barrier_async

多个线程对内存中的数组或是字典进行读的操作是OK,
但如果多个线程对内存中的数组或是字典进行读+写的操作,就会有问题

    NSMutableArray * muArr = [NSMutableArray array];
    [muArr addObject:@"1"];
    [muArr addObject:@"2"];
    [muArr addObject:@"3"];
    dispatch_queue_t con_queue = dispatch_queue_create("com.pogong.www", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(con_queue, ^{
        NSLog(@"%@",muArr);
    });
    
    dispatch_async(con_queue, ^{
        NSLog(@"%@",muArr);
    });
    
    dispatch_barrier_async(con_queue, ^{
        NSLog(@"add add add");
        [muArr addObject:@"4"];
    });
    
    dispatch_async(con_queue, ^{
        NSLog(@"%@",muArr);
    });
    
    dispatch_async(con_queue, ^{
        NSLog(@"%@",muArr);
    });

dispatch_barrier_async很好解决了多线程读写安全进行的问题,
在原有的多线程执行时,加入一个dispatch_barrier_async,
会像有栅栏一样挡住多线程执行,而先执行dispatch_barrier_async,
在执行完dispatch_barrier_async之后,再继续多线程的操作

11. dispatch_source

Dispatch Source用于监听系统的底层对象,比如文件描述符,Mach端口,信号量等。主要处理的事件如下表

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_MEMORYPRESSURE 内存情况
DISPATCH_SOURCE_TYPE_PROC   进程事件
DISPATCH_SOURCE_TYPE_READ   读数据
DISPATCH_SOURCE_TYPE_SIGNAL 信号
DISPATCH_SOURCE_TYPE_TIMER  定时器
DISPATCH_SOURCE_TYPE_VNODE  文件系统变化
DISPATCH_SOURCE_TYPE_WRITE  文件写入

方法
dispatch_source_create:创建dispatch source,创建后会处于挂起状态进行事件接收,需要设置事件处理handler进行事件处理。
dispatch_source_set_event_handler:设置事件处理handler
dispatch_source_set_cancel_handler:事件取消handler,就是在dispatch source释放前做些清理的事。
dispatch_source_cancel:关闭dispatch source,这样后续触发的事件时不去调用对应的事件处理handler,但已经在执行的handler不会被取消.

11.1 dispatch_source_set_timer

UITableView在被拖拽时NSTimer就不起作用了
必须加[[NSRunLoop currentRunLoop] addTimer:anyTimer forMode:NSRunLoopCommonModes];
而dispatch source timer与runloop是没有关系的,所以可以放心使用

dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER,0, 0, DISPATCH_TARGET_QUEUE_DEFAULT);
 dispatch_source_set_event_handler(source, ^(){
  NSLog(@"sourceTimer Time log");
 });
 dispatch_source_set_timer(source, DISPATCH_TIME_NOW,5*NSEC_PER_SEC,1*NSEC_PER_SEC);//1->源,2->开始时间,3->间隔时间,4->误差秒数
 _source = source;
 dispatch_resume(_source);

11.2 文件监听

监视文件夹内文件变化

NSURL * directoryURL = [NSURL URLWithString:_path]; // assume this is set to a directory
 int const fd = open([[directoryURL path] fileSystemRepresentation], O_EVTONLY);
 if (fd < 0) {
  char buffer[80];
  strerror_r(errno, buffer, sizeof(buffer));
  NSLog(@"Unable to open \"%@\": %s (%d)", [directoryURL path], buffer, errno);
  return;
 }
 dispatch_source_t source = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, fd,
               DISPATCH_VNODE_WRITE | DISPATCH_VNODE_DELETE, DISPATCH_TARGET_QUEUE_DEFAULT);
 dispatch_source_set_event_handler(source, ^(){
  unsigned long const data = dispatch_source_get_data(source);
  if (data & DISPATCH_VNODE_WRITE) {
   NSLog(@"The directory changed.");
  }
  if (data & DISPATCH_VNODE_DELETE) {
   NSLog(@"The directory has been deleted.");
  }
 });
 dispatch_source_set_cancel_handler(source, ^(){
  close(fd);
 });
 _source = source;
 dispatch_resume(_source);

还要注意需要用DISPATCH_VNODE_DELETE去检查监视的文件或文件夹是否被删除,如果删除了就停止监听

12. dispathc_once

dispathc_once函数可以确保某个 block 在应用程序执行的过程中只被处理一次,而且它是线程安全的。所以单例模式可以很简单的实现,写单例必备

+ (Manager *)sharedInstance {
    static Manager *sharedManagerInstance = nil;
    static dispatch_once_t once;

    dispatch_once($once, ^{
        sharedManagerInstance = [[Manager alloc] init];
    });

    return sharedManagerInstance;
}

13. dispathc_apply

13.1 并行队列完成任务,开启新任务

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(10, queue, ^(size_t index) {
    NSLog(@"%d\n",index);
});
NSLog(@"done");

打印结果为:

2018-04-13 21:40:43.454 GCDTrain[1289:42894] 0
2018-04-13 21:40:43.454 GCDTrain[1289:42937] 3
2018-04-13 21:40:43.454 GCDTrain[1289:42940] 2
2018-04-13 21:40:43.454 GCDTrain[1289:42894] 4
2018-04-13 21:40:43.454 GCDTrain[1289:42894] 5
2018-04-13 21:40:43.454 GCDTrain[1289:42938] 1
2018-04-13 21:40:43.455 GCDTrain[1289:42894] 8
2018-04-13 21:40:43.454 GCDTrain[1289:42940] 6
2018-04-13 21:40:43.455 GCDTrain[1289:42937] 7
2018-04-13 21:40:43.455 GCDTrain[1289:42938] 9
2018-04-13 21:40:43.455 GCDTrain[1289:42894] done

所以这又是一种对多线程无序的限定的API,dispatch_applyblock内的所有任务被执行完之后才会执行后面的代码,当然dispatch_apply与前面提到的所有带_wait的API一样都是阻塞当前线程的
dispatch_apply与带_wait的API也有不同,dispatch_apply比较适合做些重复的+执行次数确定的任务

13.2 防止开启线程过多

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 999; i++){
      dispatch_async(queue, ^{
         NSLog(@"%d,%@",i,[NSThread currentThread]);
      });
}
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_apply(999, queue, ^(size_t i){
     NSLog(@"%d,%@",i,[NSThread currentThread]);
});

看两份代码的打印结果可知道,不用dispatch_apply的并行+异步会开启许多线程,而我们已经知道:「使用太多线程会导致消耗大量内存」,所以在这种场景下应该使用dispatch_apply

14.dispatch_set_target_queue

14.1dispatch_set_target_queue可以设置queue的优先级

dispatch_queue_create创建队列的优先级跟global dispatch queue的默认优先级一样,假如我们需要设置队列的优先级,可以通过dispatch_set_target_queue方法

dispatch_queue_t serialQueue = dispatch_queue_create("com.pogong.www", DISPATCH_QUEUE_SERIAL);  
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0);  
dispatch_set_target_queue(serialQueue, globalQueue);
//这样serialQueue的优先级和globalQueue的优先级一样

14.2 定义队列层级关系

将多个队列用dispatch_set_target_queue设置为某个串行队列的下属队列,可以防止并行执行

  • 加队列层级关系
    dispatch_queue_t targetQueue = dispatch_queue_create("target_queue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_set_target_queue(queue1, targetQueue);//加层级关系
    dispatch_set_target_queue(queue2, targetQueue);//加层级关系
    dispatch_async(queue1, ^{
        NSLog(@"do job1");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job2");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job3");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job4");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job5");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job6");
    });
2018-04-13 22:07:40.723 GCDTrain[1400:51089] do job1
2018-04-13 22:07:40.723 GCDTrain[1400:51089] do job2
2018-04-13 22:07:40.724 GCDTrain[1400:51089] do job3
2018-04-13 22:07:40.724 GCDTrain[1400:51089] do job4
2018-04-13 22:07:40.724 GCDTrain[1400:51089] do job5
2018-04-13 22:07:40.725 GCDTrain[1400:51089] do job6
  • 不加队列层级关系
    dispatch_queue_t queue1 = dispatch_queue_create("queue1", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("queue2", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue1, ^{
        NSLog(@"do job1");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job2");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job3");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job4");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job5");
    });
    dispatch_async(queue2, ^{
        NSLog(@"do job6");
    });
2018-04-13 22:01:04.781 GCDTrain[1365:48597] do job2
2018-04-13 22:01:04.781 GCDTrain[1365:48598] do job1
2018-04-13 22:01:04.781 GCDTrain[1365:48599] do job3
2018-04-13 22:01:04.781 GCDTrain[1365:48609] do job5
2018-04-13 22:01:04.781 GCDTrain[1365:48608] do job4
2018-04-13 22:01:04.782 GCDTrain[1365:48597] do job6

打印结果一目了然


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值