GCD 多线程 队列 同步、异步之间的关系

其实线程 队列 同步异步 相关的文章很多,自己也看了很多。今天想写篇博客主要聊下他们之间的关系。

线程与进程以及程序

理解

很多同学都听说过,现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。
什么叫“多任务”呢?简单地说,就是操作系统可以同时运行多个任务。打个比方,你一边在用浏览器上网,一边在听MP3,一边在用Word赶作业,这就是多任务,至少同时有3个任务正在运行。还有很多任务悄悄地在后台同时运行着,只是桌面上没有显示而已。
现在,多核CPU已经非常普及了,但是,即使过去的单核CPU,也可以执行多任务。由于CPU执行代码都是顺序执行的,那么,单核CPU是怎么执行多任务的呢?
答案就是操作系统轮流让各个任务交替执行,任务1执行0.01秒,切换到任务2,任务2执行0.01秒,再切换到任务3,执行0.01秒……这样反复执行下去。表面上看,每个任务都是交替执行的,但是,由于CPU的执行速度实在是太快了,我们感觉就像所有任务都在同时执行一样。
真正的并行执行多任务只能在多核CPU上实现,但是,由于任务数量远远多于CPU的核心数量,所以,操作系统也会自动把很多任务轮流调度到每个核心上执行。
对于操作系统来说,一个任务就是一个进程(Process),比如打开一个浏览器就是启动一个浏览器进程,打开一个记事本就启动了一个记事本进程,打开两个记事本就启动了两个记事本进程,打开一个Word就启动了一个Word进程。
有些进程还不止同时干一件事,比如Word,它可以同时进行打字、拼写检查、打印等事情。在一个进程内部,要同时干多件事,就需要同时运行多个“子任务”,我们把进程内的这些“子任务”称为线程(Thread)。
由于每个进程至少要干一件事,所以,一个进程至少有一个线程。当然,像Word这种复杂的进程可以有多个线程,多个线程可以同时执行,多线程的执行方式和多进程是一样的,也是由操作系统在多个线程之间快速切换,让每个线程都短暂地交替运行,看起来就像同时执行一样。当然,真正地同时执行多线程需要多核CPU才可能实现。

具体概念

进程

狭义定义:进程就是一段程序的执行过程。
广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。
简单的来讲进程的概念主要有两点:第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时,它才能成为一个活动的实体,我们称其为进程。

线程

通常在一个进程中可以包含若干个线程,当然一个进程中至少有一个线程,不然没有存在的意义。线程可以利用进程所拥有的资源,在引入线程的操作系统中,通常都是把进程作为分配资源的基本单位,而把线程作为独立运行和独立调度的基本单位,由于线程比进程更小,基本上不拥有系统资源,故对它的调度所付出的开销就会小得多,能更高效的提高系统多个程序间并发执行的程度。

多线程

在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统的效率。线程是在同一时间需要完成多项任务的时候实现的。

程序

说起进程,就不得不说下程序。先看定义:程序是指令和数据的有序集合,其本身没有任何运行的含义,是一个静态的概念。而进程则是在处理机上的一次执行过程,它是一个动态的概念。这个不难理解,其实进程是包含程序的,进程的执行离不开程序,进程中的文本区域就是代码区,也就是程序。

进程与线程的区别

进程和线程的主要差别在于它们是不同的操作系统资源管理方式。进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响,而线程只是一个进程中的不同执行路径。线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间,一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。

  1. 简而言之,一个程序至少有一个进程,一个进程至少有一个线程.
  2. 线程的划分尺度小于进程,使得多线程程序的并发性高。
  3. 另外,进程在执行过程中拥有独立的内存单元,而多个线程共享内存,从而极大地提高了程序的运行效率。
  4. 线程在执行过程中与进程还是有区别的。每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口。但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
  5. 从逻辑角度来看,多线程的意义在于一个应用程序中,有多个执行部分可以同时执行。但操作系统并没有将多个线程看做多个独立的应用,来实现进程的调度和管理以及资源分配。这就是进程和线程的重要区别。

队列

概念

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。

队列是 FIFO:FrontInputFrontOut

task 入队表演



task 出队表演


GCD 队列

  • 串行队列 (SerierQueue)
  • 并行队列 (concurrentQueue)

Concurrent: tasks are dequeued in FIFO order, but run concurrently and can finish in any order. 一次可以执行多个task。
Serial: tasks execute one at a time in FIFO order 一次只能执行一个task

同步 异步

同步(Sync)

所谓同步,就是发出一个功能调用时,在没有得到结果之前,该调用就不返回或继续执行后续操作。

异步(Async)

异步与同步相对,当一个异步过程调用发出后,调用者在没有得到结果之前,就可以继续执行后续操作。当这个调用完成后,一般通过状态、通知和回调来通知调用者。对于异步调用,调用的返回并不受调用者控制。

关系

其实我们可以把

  • queue 当做是管道容器
  • 容器里面可以装有货物(task)
  • 线程(thread)相当于生产线,多线程相当于有多条生产线
  • 同步异步相当于货物(task) 在生产线上是否生成完成
  • 串行队列和并行队列是对货物的处理方式,串行队列相当于只允许一条生产线(thread)处理队列的任务(task),并行队列相当于允许多条生产线(thread)处理队列的任务

从上面看我们想执行一个任务task ,有两个要素,queue(容器)和对task的处理方式(同步和异步)。(不需要thread,因为queue是自动选择生产线的thread

因此对任务的处理排列组合有四种情况

  • 同步+串行队列
  • 异步+串行队列
  • 同步+并行队列
  • 异步+并行队列

同步+串行队列 的线程选择情况

void syncSerial() {
    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建串行队列 */
    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("SerialQueue2", DISPATCH_QUEUE_SERIAL);

    /* 2. 将任务放到队列中 */
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue task1 exe thread--------%@",[NSThread currentThread]);
    });

    dispatch_sync(serialQueue2, ^{
        NSLog(@"serialQueue2 task1 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue task2 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue2, ^{
        NSLog(@"serialQueue2 task2 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue task3 exe thread--------%@",[NSThread currentThread]);
    });
}

执行结果

2018-09-21 10:31:57.620658+0800 TestCommand[13548:18058898] current thread <NSThread: 0x100503e90>{number = 1, name = main}
2018-09-21 10:31:57.621145+0800 TestCommand[13548:18058898] serialQueue task1 exe thread--------<NSThread: 0x100503e90>{number = 1, name = main}
2018-09-21 10:31:57.621186+0800 TestCommand[13548:18058898] serialQueue2 task1 exe thread--------<NSThread: 0x100503e90>{number = 1, name = main}
2018-09-21 10:31:57.621214+0800 TestCommand[13548:18058898] serialQueue task2 exe thread--------<NSThread: 0x100503e90>{number = 1, name = main}
2018-09-21 10:31:57.621238+0800 TestCommand[13548:18058898] serialQueue2 task2 exe thread--------<NSThread: 0x100503e90>{number = 1, name = main}
2018-09-21 10:31:57.621306+0800 TestCommand[13548:18058898] serialQueue task3 exe thread--------<NSThread: 0x100503e90>{number = 1, name = main}
同步执行

串行队列同步执行任务,只会在当前线程中将队列的任务按照加入的顺序依次执行完毕。
这里我们发现,queue这时候的选择thread是 当前thread

串行队列中的特殊队列 主队列MainQueue

    NSLog(@"current thread %@",[NSThread currentThread]);
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
   dispatch_sync(mainQueue, ^{
        NSLog(@"mainQueue task3 exe thread--------%@",[NSThread currentThread]);
    });
2018-09-21 10:47:36.427999+0800 TestCommand[13871:18069254] current thread <NSThread: 0x100607ca0>{number = 1, name = main}

我们发现在主线程中同步执行主队列的任务会发生崩溃。大家都知道是发生了死锁。
这里需要探讨下,什么时候线程和queue绑定在一起了。
看下面的例子

void syncSerial() {
    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建串行队列 */
    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_SERIAL);
    /* 2. 将任务放到队列中 */
    dispatch_sync(serialQueue, ^{
        NSLog(@"serialQueue task1 exe thread--------%@",[NSThread currentThread]);
        dispatch_sync(serialQueue, ^{
            NSLog(@"serialQueue task1 exe thread--------%@",[NSThread currentThread]);
        });
    });
}
2018-09-21 11:06:53.339399+0800 TestCommand[14303:18083785] current thread <NSThread: 0x10040ddf0>{number = 1, name = main}
2018-09-21 11:06:53.339847+0800 TestCommand[14303:18083785] serialQueue task1 exe thread--------<NSThread: 0x10040ddf0>{number = 1, name = main}

同样发生了崩溃。
我们发现在主线程中调用 同步sync调用串行队列serialQueue ,没有发生崩溃。而在串行队列的task中再次调用 同步sync调用串行队列serialQueue执行任务发生崩溃了,但是串行队列的task执行是在主线程中的。为什么第一次调用主队列调用task没问题,而第二次执行执行task发生崩溃了呢?

我们发现在执行task1 的时候,在serierQueue中添加了task2,同时要求执行task2,这个时候,task1还没有完成,这违背了串行队列的含义了,FIFO,崩溃了。
其实主线程也是同样的道理,我们可以把main函数当做mainQueue需要执行的任务task,在主线程的task没有执行完毕是不允许想mainqueue中继续添加task的。借鉴下别人的图

异步+串行队列 的线程选择情况

void asyncSerial() {
    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建串行队列 */
    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("SerialQueue2", DISPATCH_QUEUE_SERIAL);
    
    /* 2. 将任务放到队列中 */
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue task1 exe thread--------%@",[NSThread currentThread]);
    });
    
    dispatch_async(serialQueue2, ^{
        NSLog(@"serialQueue2 task1 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue task2 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue2, ^{
        NSLog(@"serialQueue2 task2 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_async(serialQueue, ^{
        NSLog(@"serialQueue task3 exe thread--------%@",[NSThread currentThread]);
    });
}

测试结果

void asyncSerial() {
    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建串行队列 */
    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("SerialQueue2", DISPATCH_QUEUE_SERIAL);
    
    for (int i=0; i<100; i++) {
        /* 2. 将任务放到队列中 */
        dispatch_async(serialQueue, ^{
            NSLog(@"== %d serialQueue task1 exe thread--------%@",i,[NSThread currentThread]);
        });
        
        dispatch_async(serialQueue2, ^{
            NSLog(@"++ %d serialQueue2 task1 exe thread--------%@",i,[NSThread currentThread]);
        });
        dispatch_async(serialQueue, ^{
            NSLog(@"== %d serialQueue task2 exe thread--------%@",i,[NSThread currentThread]);
        });
        dispatch_async(serialQueue2, ^{
            NSLog(@"++ %d serialQueue2 task2 exe thread--------%@",i,[NSThread currentThread]);
        });
        dispatch_async(serialQueue, ^{
            NSLog(@"== %d serialQueue task3 exe thread--------%@",i,[NSThread currentThread]);
        });
    }
}
  

部分日志

2018-09-21 11:50:30.880113+0800 TestCommand[15342:18116065] == 0 serialQueue task1 exe thread--------<NSThread: 0x100620500>{number = 2, name = (null)}
2018-09-21 11:50:30.880212+0800 TestCommand[15342:18116065] == 0 serialQueue task2 exe thread--------<NSThread: 0x100620500>{number = 2, name = (null)}
2018-09-21 11:50:30.880309+0800 TestCommand[15342:18116065] == 0 serialQueue task3 exe thread--------<NSThread: 0x100620500>{number = 2, name = (null)}
2018-09-21 11:50:30.880348+0800 TestCommand[15342:18116065] == 1 serialQueue task1 exe thread--------<NSThread: 0x100620500>{number = 2, name = (null)}
2018-09-21 11:50:30.880421+0800 TestCommand[15342:18116064] == 1 serialQueue task2 exe thread--------<NSThread: 0x100512c10>{number = 3, name = (null)}
2018-09-21 11:50:30.881062+0800 TestCommand[15342:18116064] == 3 serialQueue task2 exe thread--------<NSThread: 0x100512c10>{number = 3, name = (null)}
098+0800 TestCommand[15342:18116064] == 3 serialQueue task3 exe thread--------<NSThread: 0x100512c10>{number = 3, name = (null)}

我们发现异步执行串行队列,串行队列的thread不是主线程了,而是一条新的线程,因此,串行队列在异步执行是开启了一条新的线程,串行队列对应的任务可能在多条线程中执行。串行队列的开辟的线程之间是可以共享使用的。

主队列在主线程中执行异步什么情况呢

void asyncSerial() {
    
    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建串行队列 */
  dispatch_queue_t mainQueue =  dispatch_get_main_queue();
 
    
    dispatch_queue_t serialQueue = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t serialQueue2 = dispatch_queue_create("SerialQueue2", DISPATCH_QUEUE_SERIAL);
    
    for (int i=0; i<100; i++) {
        dispatch_async(mainQueue, ^{
            NSLog(@" mainQueue task2 exe thread--------%@",[NSThread currentThread]);
        });
        /* 2. 将任务放到队列中 */
        dispatch_async(serialQueue, ^{
            NSLog(@"== %d serialQueue task1 exe thread--------%@",i,[NSThread currentThread]);
        });

        dispatch_async(serialQueue2, ^{
            NSLog(@"++ %d serialQueue2 task1 exe thread--------%@",i,[NSThread currentThread]);
        });
        dispatch_async(serialQueue, ^{
            NSLog(@"== %d serialQueue task2 exe thread--------%@",i,[NSThread currentThread]);
        });
        dispatch_async(serialQueue2, ^{
            NSLog(@"++ %d serialQueue2 task2 exe thread--------%@",i,[NSThread currentThread]);
        });
        dispatch_async(serialQueue, ^{
            NSLog(@"== %d serialQueue task3 exe thread--------%@",i,[NSThread currentThread]);
        });
    }
}
2018-09-21 14:04:54.194959+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.195007+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.195179+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x10042018-09-21 14:04:54.195211+0800 TestCommand[18005:18198570] == 47 serialQueue task1 exe thread--------<NSThread: 0x100506bc0>{number = 2, name = (null)}
2018-09-21 14:04:54.195357+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.195390+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.195420+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.195655+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.195691+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.205288+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x1004082018-09-21 14:04:54.205337+0800 TestCommand[18005:18198570] == 49 serialQueue task3 exe thread--------<NSThread: 0x100506bc0>{number = 2, name = (null)}
2018-09-21 14:04:54.205492+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.205768+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.205812+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x10040892018-09-21 14:04:54.205858+0800 TestCommand[18005:18198570] == 52 serialQueue task3 exe thread--------<NSThread: 0x100506bc0>{number = 2, name = (null)}
2018-09-21 14:04:54.205987+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.206066+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.206078+0800 TestCommand[18005:18198570] == 54 serialQueue task2 exe thread-2018-09-21 14:04:54.206139+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.206259+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}
2018-09-21 14:04:54.206078+0800 TestCommand[18005:18198570] == 54 serialQueue task2 exe thread-2018-09-21 14:04:54.206139+0800 TestCommand[18005:18198528]  mainQueue task2 exe thread--------<NSThread: 0x100408940>{number = 1, name = main}

颠覆三观啊。主线程上竟然也运行了其他队列的任务,其他线程也有运行主队列的任务。

不过这里要说明下,大多数情况下,主队列的任务都是运行在主线程上的。
各自的串行队列异步也都是运行在各自第一次创建的子线程上。
主队列是不会创建新的线程的,用的是主线程执行任务。(这里必须是开启runloop的情况下,否则和其他子线程是一样的,开启新的线程,具体为什么可以看上篇文章iOS 主程序和主队列的区别

同步+并行队列 的线程选择情况

void syncConcurrent() {
    
    NSLog(@"current thread %@",[NSThread currentThread]);

    /* 1. 创建一条并发队列 */
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t concurrentQueue2 = dispatch_queue_create("concurrentQueue2", DISPATCH_QUEUE_CONCURRENT);

    /* 2. 把任务放到队列中 */
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"concurrentQueue task1 exe thread--------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(concurrentQueue2, ^{
        NSLog(@"concurrentQueue2 task1 exe thread--------%@",[NSThread currentThread]);
    });
    
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"concurrentQueue task2 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_sync(concurrentQueue2, ^{
        NSLog(@"concurrentQueue2 task2 exe thread--------%@",[NSThread currentThread]);
    });
    
    
    dispatch_sync(concurrentQueue, ^{
        NSLog(@"concurrentQueue task3 exe thread--------%@",[NSThread currentThread]);
    });
    
}

执行结果

2020-11-27 14:06:44.976401+0800 HelloWorld[68094:9169914] current thread <NSThread: 0x600002728a00>{number = 1, name = main}
2020-11-27 14:06:44.976554+0800 HelloWorld[68094:9169914] concurrentQueue task1 exe thread--------<NSThread: 0x600002728a00>{number = 1, name = main}
2020-11-27 14:06:44.976653+0800 HelloWorld[68094:9169914] concurrentQueue2 task1 exe thread--------<NSThread: 0x600002728a00>{number = 1, name = main}
2020-11-27 14:06:44.976769+0800 HelloWorld[68094:9169914] concurrentQueue task2 exe thread--------<NSThread: 0x600002728a00>{number = 1, name = main}
2020-11-27 14:06:44.976902+0800 HelloWorld[68094:9169914] concurrentQueue2 task2 exe thread--------<NSThread: 0x600002728a00>{number = 1, name = main}
2020-11-27 14:06:44.977007+0800 HelloWorld[68094:9169914] concurrentQueue task3 exe thread--------<NSThread: 0x600002728a00>{number = 1, name = main}

并行队列同步执行,就是在当前线程中按照顺序依次执行,没有创建任何新的线程。和执行串行队列效果一样的。

异步+并行队列 的线程选择情况

void asyncConcurrent() {
    NSLog(@"current thread %@",[NSThread currentThread]);
    
    /* 1. 创建一条并发队列 */
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t concurrentQueue2 = dispatch_queue_create("concurrentQueue2", DISPATCH_QUEUE_CONCURRENT);
    
    /* 2. 把任务放到队列中 */
    dispatch_async(concurrentQueue, ^{
        NSLog(@"concurrentQueue task1 exe thread--------%@",[NSThread currentThread]);
    });
    
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"concurrentQueue2 task1 exe thread--------%@",[NSThread currentThread]);
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"concurrentQueue task2 exe thread--------%@",[NSThread currentThread]);
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"concurrentQueue2 task2 exe thread--------%@",[NSThread currentThread]);
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"concurrentQueue task3 exe thread--------%@",[NSThread currentThread]);
    });
}

执行结果

2018-09-21 15:04:19.960120+0800 TestCommand[19545:18244967] current thread <NSThread: 0x100402b00>{number = 1, name = main}
2018-09-21 15:04:19.960668+0800 TestCommand[19545:18245019] concurrentQueue task1 exe thread--------<NSThread: 0x100410630>{number = 3, name = (null)}
2018-09-21 15:04:19.960668+0800 TestCommand[19545:18245017] concurrentQueue2 task1 exe thread--------<NSThread: 0x1005115b0>{number = 2, name = (null)}
2018-09-21 15:04:19.960704+0800 TestCommand[19545:18245018] concurrentQueue task2 exe thread--------<NSThread: 0x100664d60>{number = 4, name = (null)}
2018-09-21 15:04:19.960736+0800 TestCommand[19545:18245017] concurrentQueue2 task2 exe thread--------<NSThread: 0x1005115b0>{number = 2, name = (null)}
2018-09-21 15:04:19.960830+0800 TestCommand[19545:18245018] concurrentQueue task3 exe thread--------<NSThread: 0x100664d60>{number = 4, name = (null)}


并发队列concurrentQueue分别执行在了三条线程上
并发队列concurrentQueue2分别执行在两条线程上
并发队列创建的线程之间是可以共享使用的。我们看看什么时候不会创建新的线程。

猜猜,当并行队列开辟的线程有任务在执行,再来新任务就会开辟新的线程。

static void blockCleanUp(__strong void(^*block)(void)) {
    (*block)();
}

#define onExit \
__strong void(^block)(void) __attribute__((cleanup(blockCleanUp), unused)) = ^

void asyncConcurrent() {
    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建一条并发队列 */
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_queue_t concurrentQueue2 = dispatch_queue_create("concurrentQueue2", DISPATCH_QUEUE_CONCURRENT);
    
    /* 2. 把任务放到队列中 */
    dispatch_async(concurrentQueue, ^{
        NSLog(@"concurrentQueue task1 exe thread--------%@",[NSThread currentThread]);
        onExit{
            NSLog(@"concurrentQueue task1 exit  %@",[NSThread currentThread]);
        };
    });
    
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"concurrentQueue2 task1 exe thread--------%@",[NSThread currentThread]);
        onExit{
            NSLog(@"concurrentQueue2 task1 exit  %@",[NSThread currentThread]);
        };
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"concurrentQueue task2 exe thread--------%@",[NSThread currentThread]);
        onExit{
            NSLog(@"concurrentQueue task2 exit  %@",[NSThread currentThread]);
        };
    });
    dispatch_async(concurrentQueue2, ^{
        NSLog(@"concurrentQueue2 task2 exe thread--------%@",[NSThread currentThread]);
        onExit{
            NSLog(@"concurrentQueue2 task2 exit %@",[NSThread currentThread]);
        };
    });
    
    dispatch_async(concurrentQueue, ^{
        NSLog(@"concurrentQueue task3 exe thread--------%@",[NSThread currentThread]);
        onExit{
            NSLog(@"concurrentQueue task3 exit %@",[NSThread currentThread]);
        };
    });
}

执行结果

2018-09-21 15:19:04.520132+0800 TestCommand[19907:18256683] current thread <NSThread: 0x100703760>{number = 1, name = main}
2018-09-21 15:19:04.520964+0800 TestCommand[19907:18256725] concurrentQueue2 task1 exe thread--------<NSThread: 0x100402eb0>{number = 2, name = (null)}
2018-09-21 15:19:04.520965+0800 TestCommand[19907:18256734] concurrentQueue2 task2 exe thread--------<NSThread: 0x1007001f0>{number = 4, name = (null)}
2018-09-21 15:19:04.520998+0800 TestCommand[19907:18256726] concurrentQueue task1 exe thread--------<NSThread: 0x100426d60>{number = 5, name = (null)}
2018-09-21 15:19:04.521014+0800 TestCommand[19907:18256724] concurrentQueue task2 exe thread--------<NSThread: 0x100426140>{number = 3, name = (null)}
2018-09-21 15:19:04.521104+0800 TestCommand[19907:18256725] concurrentQueue2 task1 exit  <NSThread: 0x100402eb0>{number = 2, name = (null)}
2018-09-21 15:19:04.521107+0800 TestCommand[19907:18256734] concurrentQueue2 task2 exit <NSThread: 0x1007001f0>{number = 4, name = (null)}
2018-09-21 15:19:04.521138+0800 TestCommand[19907:18256726] concurrentQueue task1 exit  <NSThread: 0x100426d60>{number = 5, name = (null)}
2018-09-21 15:19:04.521320+0800 TestCommand[19907:18256724] concurrentQueue task2 exit  <NSThread: 0x100426140>{number = 3, name = (null)}
2018-09-21 15:19:04.522003+0800 TestCommand[19907:18256724] concurrentQueue task3 exe thread--------<NSThread: 0x100426140>{number = 3, name = (null)}
2018-09-21 15:19:04.522049+0800 TestCommand[19907:18256724] concurrentQueue task3 exit <NSThread: 0x100426140>{number = 3, name = (null)}

这里线程3 被复用了。这是因为线程3 已经执行完毕了concurrentQueue task2的任务空闲下来了。这里我们可以测试下,系统最多能开辟多少条线程

    NSLog(@"current thread %@",[NSThread currentThread]);
    /* 1. 创建一条并发队列 */
    dispatch_queue_t concurrentQueue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i =0; i<1000; i++) {
        dispatch_async(concurrentQueue, ^{
            
            NSLog(@"%d concurrentQueue task1 exe thread--------%@",i,[NSThread currentThread]);
            sleep(10);
            onExit{
                NSLog(@" %d concurrentQueue task1 exit  %@",i,[NSThread currentThread]);
            };
        });
    }
测试结果
2018-09-21 15:26:12.902174+0800 TestCommand[20107:18262777] current thread <NSThread: 0x100502420>{number = 1, name = main}
2018-09-21 15:26:12.902962+0800 TestCommand[20107:18262833] 0 concurrentQueue task1 exe thread--------<NSThread: 0x1005290a0>{number = 2, name = (null)}
2018-09-21 15:26:12.903393+0800 TestCommand[20107:18262831] 2 concurrentQueue task1 exe thread--------<NSThread: 0x100441cf0>{number = 3, name = (null)}
2018-09-21 15:26:12.903500+0800 TestCommand[20107:18262835] 3 concurrentQueue task1 exe thread--------<NSThread: 0x10071ef10>{number = 4, name = (null)}
2018-09-21 15:26:12.903608+0800 TestCommand[20107:18262832] 1 concurrentQueue task1 exe thread--------<NSThread: 0x1005028f0>{number = 5, name = (null)}
2018-09-21 15:26:12.903709+0800 TestCommand[20107:18262836] 4 concurrentQueue task1 exe thread--------<NSThread: 0x100529cd0>{number = 7, name = (null)}
2018-09-21 15:26:12.903729+0800 TestCommand[20107:18262837] 5 concurrentQueue task1 exe thread--------<NSThread: 0x10071edb0>{number = 6, name = (null)}
2018-09-21 15:26:12.904040+0800 TestCommand[20107:18262838] 6 concurrentQueue task1 exe thread--------<NSThread: 0x100441dd0>{number = 8, name = (null)}
2018-09-21 15:26:12.904050+0800 TestCommand[20107:18262839] 7 concurrentQueue task1 exe thread--------<NSThread: 0x10071ed00>{number = 9, name = (null)}
2018-09-21 15:26:12.904212+0800 TestCommand[20107:18262840] 8 concurrentQueue task1 exe thread--------<NSThread: 0x100529fb0>{number = 10, name = (null)}
2018-09-21 15:26:12.904293+0800 TestCommand[20107:18262841] 9 concurrentQueue task1 exe thread--------<NSThread: 0x10071ed60>{number = 11, name = (null)}
2018-09-21 15:26:12.904433+0800 TestCommand[20107:18262842] 10 concurrentQueue task1 exe thread--------<NSThread: 0x10071f1b0>{number = 12, name = (null)}
2018-09-21 15:26:12.904712+0800 TestCommand[20107:18262844] 12 concurrentQueue task1 exe thread--------<NSThread: 0x10071f550>{number = 14, name = (null)}
2018-09-21 15:26:12.904810+0800 TestCommand[20107:18262843] 11 concurrentQueue task1 exe thread--------<NSThread: 0x100529ed0>{number = 13, name = (null)}
2018-09-21 15:26:12.905110+0800 TestCommand[20107:18262845] 13 concurrentQueue task1 exe thread--------<NSThread: 0x100529f70>{number = 15, name = (null)}
2018-09-21 15:26:12.905351+0800 TestCommand[20107:18262847] 15 concurrentQueue task1 exe thread--------<NSThread: 0x10071f630>{number = 17, name = (null)}
2018-09-21 15:26:12.905384+0800 TestCommand[20107:18262846] 14 concurrentQueue task1 exe thread--------<NSThread: 0x100529f10>{number = 16, name = (null)}
2018-09-21 15:26:12.905557+0800 TestCommand[20107:18262848] 16 concurrentQueue task1 exe thread--------<NSThread: 0x10061ae80>{number = 18, name = (null)}
2018-09-21 15:26:12.905692+0800 TestCommand[20107:18262849] 17 concurrentQueue task1 exe thread--------<NSThread: 0x100441f30>{number = 19, name = (null)}
2018-09-21 15:26:12.905714+0800 TestCommand[20107:18262850] 18 concurrentQueue task1 exe thread--------<NSThread: 0x10052b390>{number = 20, name = (null)}
2018-09-21 15:26:12.905973+0800 TestCommand[20107:18262851] 19 concurrentQueue task1 exe thread--------<NSThread: 0x10061af60>{number = 21, name = (null)}
2018-09-21 15:26:12.906198+0800 TestCommand[20107:18262852] 20 concurrentQueue task1 exe thread--------<NSThread: 0x10071f3a0>{number = 22, name = (null)}
2018-09-21 15:26:12.906530+0800 TestCommand[20107:18262853] 21 concurrentQueue task1 exe thread--------<NSThread: 0x10052b1c0>{number = 23, name = (null)}
2018-09-21 15:26:12.906560+0800 TestCommand[20107:18262854] 22 concurrentQueue task1 exe thread--------<NSThread: 0x10052b2a0>{number = 24, name = (null)}
2018-09-21 15:26:12.906725+0800 TestCommand[20107:18262855] 23 concurrentQueue task1 exe thread--------<NSThread: 0x100442060>{number = 25, name = (null)}
2018-09-21 15:26:12.906725+0800 TestCommand[20107:18262856] 24 concurrentQueue task1 exe thread--------<NSThread: 0x10052bb40>{number = 26, name = (null)}
2018-09-21 15:26:12.906869+0800 TestCommand[20107:18262857] 25 concurrentQueue task1 exe thread--------<NSThread: 0x10071f4a0>{number = 27, name = (null)}
2018-09-21 15:26:12.906904+0800 TestCommand[20107:18262859] 27 concurrentQueue task1 exe thread--------<NSThread: 0x10071f720>{number = 29, name = (null)}
2018-09-21 15:26:12.906971+0800 TestCommand[20107:18262858] 26 concurrentQueue task1 exe thread--------<NSThread: 0x100442170>{number = 28, name = (null)}
2018-09-21 15:26:12.907190+0800 TestCommand[20107:18262860] 28 concurrentQueue task1 exe thread--------<NSThread: 0x10052bfc0>{number = 30, name = (null)}
2018-09-21 15:26:12.907271+0800 TestCommand[20107:18262861] 29 concurrentQueue task1 exe thread--------<NSThread: 0x10071fff0>{number = 31, name = (null)}
2018-09-21 15:26:12.907378+0800 TestCommand[20107:18262862] 30 concurrentQueue task1 exe thread--------<NSThread: 0x10061b060>{number = 33, name = (null)}
2018-09-21 15:26:12.907381+0800 TestCommand[20107:18262864] 32 concurrentQueue task1 exe thread--------<NSThread: 0x10071fae0>{number = 34, name = (null)}
2018-09-21 15:26:12.907445+0800 TestCommand[20107:18262863] 31 concurrentQueue task1 exe thread--------<NSThread: 0x100720270>{number = 32, name = (null)}
2018-09-21 15:26:12.907728+0800 TestCommand[20107:18262866] 34 concurrentQueue task1 exe thread--------<NSThread: 0x10052c250>{number = 36, name = (null)}
2018-09-21 15:26:12.907773+0800 TestCommand[20107:18262865] 33 concurrentQueue task1 exe thread--------<NSThread: 0x10061b5e0>{number = 35, name = (null)}
2018-09-21 15:26:12.907805+0800 TestCommand[20107:18262867] 35 concurrentQueue task1 exe thread--------<NSThread: 0x100442250>{number = 37, name = (null)}
2018-09-21 15:26:12.907851+0800 TestCommand[20107:18262868] 36 concurrentQueue task1 exe thread--------<NSThread: 0x1007201f0>{number = 38, name = (null)}
2018-09-21 15:26:12.908311+0800 TestCommand[20107:18262869] 37 concurrentQueue task1 exe thread--------<NSThread: 0x10061b6e0>{number = 40, name = (null)}
2018-09-21 15:26:12.908364+0800 TestCommand[20107:18262870] 38 concurrentQueue task1 exe thread--------<NSThread: 0x100442560>{number = 39, name = (null)}
2018-09-21 15:26:12.908614+0800 TestCommand[20107:18262871] 39 concurrentQueue task1 exe thread--------<NSThread: 0x100442410>{number = 41, name = (null)}
2018-09-21 15:26:12.908766+0800 TestCommand[20107:18262872] 41 concurrentQueue task1 exe thread--------<NSThread: 0x100720400>{number = 43, name = (null)}
2018-09-21 15:26:12.908733+0800 TestCommand[20107:18262873] 40 concurrentQueue task1 exe thread--------<NSThread: 0x100442840>{number = 42, name = (null)}
2018-09-21 15:26:12.909024+0800 TestCommand[20107:18262874] 42 concurrentQueue task1 exe thread--------<NSThread: 0x10061b7e0>{number = 44, name = (null)}
2018-09-21 15:26:12.909106+0800 TestCommand[20107:18262876] 44 concurrentQueue task1 exe thread--------<NSThread: 0x100720a90>{number = 46, name = (null)}
2018-09-21 15:26:12.909192+0800 TestCommand[20107:18262877] 45 concurrentQueue task1 exe thread--------<NSThread: 0x10061b9e0>{number = 47, name = (null)}
2018-09-21 15:26:12.909278+0800 TestCommand[20107:18262875] 43 concurrentQueue task1 exe thread--------<NSThread: 0x10061b8e0>{number = 45, name = (null)}
2018-09-21 15:26:12.909538+0800 TestCommand[20107:18262878] 46 concurrentQueue task1 exe thread--------<NSThread: 0x100721030>{number = 49, name = (null)}
2018-09-21 15:26:12.909564+0800 TestCommand[20107:18262879] 47 concurrentQueue task1 exe thread--------<NSThread: 0x10061bae0>{number = 48, name = (null)}
2018-09-21 15:26:12.909718+0800 TestCommand[20107:18262881] 49 concurrentQueue task1 exe thread--------<NSThread: 0x100442920>{number = 51, name = (null)}
2018-09-21 15:26:12.909765+0800 TestCommand[20107:18262880] 48 concurrentQueue task1 exe thread--------<NSThread: 0x100720fd0>{number = 50, name = (null)}
2018-09-21 15:26:12.909853+0800 TestCommand[20107:18262882] 50 concurrentQueue task1 exe thread--------<NSThread: 0x10061bb70>{number = 52, name = (null)}
2018-09-21 15:26:12.910002+0800 TestCommand[20107:18262883] 51 concurrentQueue task1 exe thread--------<NSThread: 0x100442a00>{number = 53, name = (null)}
2018-09-21 15:26:12.910094+0800 TestCommand[20107:18262884] 52 concurrentQueue task1 exe thread--------<NSThread: 0x10061bb20>{number = 54, name = (null)}
2018-09-21 15:26:12.910219+0800 TestCommand[20107:18262885] 53 concurrentQueue task1 exe thread--------<NSThread: 0x100721210>{number = 55, name = (null)}
2018-09-21 15:26:12.910273+0800 TestCommand[20107:18262886] 54 concurrentQueue task1 exe thread--------<NSThread: 0x10052cf50>{number = 56, name = (null)}
2018-09-21 15:26:12.910295+0800 TestCommand[20107:18262887] 55 concurrentQueue task1 exe thread--------<NSThread: 0x100721110>{number = 57, name = (null)}
2018-09-21 15:26:12.910560+0800 TestCommand[20107:18262888] 56 concurrentQueue task1 exe thread--------<NSThread: 0x100442b00>{number = 58, name = (null)}
2018-09-21 15:26:12.910572+0800 TestCommand[20107:18262889] 57 concurrentQueue task1 exe thread--------<NSThread: 0x100442c00>{number = 59, name = (null)}
2018-09-21 15:26:12.910794+0800 TestCommand[20107:18262891] 59 concurrentQueue task1 exe thread--------<NSThread: 0x1007214f0>{number = 61, name = (null)}
2018-09-21 15:26:12.910822+0800 TestCommand[20107:18262892] 60 concurrentQueue task1 exe thread--------<NSThread: 0x10052e070>{number = 62, name = (null)}
2018-09-21 15:26:12.910877+0800 TestCommand[20107:18262890] 58 concurrentQueue task1 exe thread--------<NSThread: 0x1007212f0>{number = 60, name = (null)}
2018-09-21 15:26:12.910966+0800 TestCommand[20107:18262893] 61 concurrentQueue task1 exe thread--------<NSThread: 0x10061b200>{number = 63, name = (null)}
2018-09-21 15:26:12.910985+0800 TestCommand[20107:18262894] 62 concurrentQueue task1 exe thread--------<NSThread: 0x10052e150>{number = 64, name = (null)}
2018-09-21 15:26:12.911036+0800 TestCommand[20107:18262895] 63 concurrentQueue task1 exe thread--------<NSThread: 0x1007213f0>{number = 65, name = (null)}
2018-09-21 15:26:22.905037+0800 TestCommand[20107:18262831]  2 concurrentQueue task1 exit  <NSThread: 0x100441cf0>{number = 3, name = (null)}
2018-09-21 15:26:22.905037+0800 TestCommand[20107:18262835]  3 concurrentQueue task1 exit  <NSThread: 0x10071ef10>{number = 4, name = (null)}
2018-09-21 15:26:22.905105+0800 TestCommand[20107:18262836]  4 concurrentQueue task1 exit  <NSThread: 0x100529cd0>{number = 7, name = (null)}
2018-09-21 15:26:22.905109+0800 TestCommand[20107:18262832]  1 concurrentQueue task1 exit  <NSThread: 0x1005028f0>{number = 5, name = (null)}
2018-09-21 15:26:22.905152+0800 TestCommand[20107:18262836] 65 concurrentQueue task1 exe thread--------<NSThread: 0x100529cd0>{number = 7, name = (null)}
2018-09-21 15:26:22.905154+0800 TestCommand[20107:18262832] 66 concurrentQueue task1 exe thread--------<NSThread: 0x1005028f0>{number = 5, name = (null)}
2018-09-21 15:26:22.905154+0800 TestCommand[20107:18262831] 64 concurrentQueue task1 exe thread--------<NSThread: 0x100441cf0>{number = 3, name = (null)}

我们发现系统最多给我们开辟64条线程,并不是无限开辟。

汇总

补充一句,这里的主队列是必须开启runloop才有的行为,要不和普通的线程是没有区别的。

全局并发队列

首先, 它是一个并发队列, 他是系统为我们创建好的一个全局的并发队列, 所以, 有时候, 我们不需要自己创建一个并发队列, 直接用系统为我们提供的全局队列就可以了,所以全局队列和同步执行以及异步执行的组合同并发队列是一样的。

同步,异步,串行队列,并发队列,全局队列,主队列等概念的总结

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,013评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,205评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,370评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,168评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,153评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,954评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,271评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,916评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,382评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,877评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,989评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,624评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,209评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,199评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,418评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,401评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,700评论 2 345

推荐阅读更多精彩内容

  • 本文是我自己在秋招复习时的读书笔记,整理的知识点,也是为了防止忘记,尊重劳动成果,转载注明出处哦!如果你也喜欢,那...
    波波波先森阅读 11,235评论 4 56
  • iOS多线程编程 基本知识 1. 进程(process) 进程是指在系统中正在运行的一个应用程序,就是一段程序的执...
    陵无山阅读 6,004评论 1 14
  • 1.ios高性能编程 (1).内层 最小的内层平均值和峰值(2).耗电量 高效的算法和数据结构(3).初始化时...
    欧辰_OSR阅读 29,320评论 8 265
  • 从哪说起呢? 单纯讲多线程编程真的不知道从哪下嘴。。 不如我直接引用一个最简单的问题,以这个作为切入点好了 在ma...
    Mr_Baymax阅读 2,734评论 1 17
  • 这个是个周六的下午,很长一段时间没有写点文字,特别想写些随笔,于是想到了简书。 所以,这是我在简书记录心情的第一天...
    墨白的碎碎念阅读 166评论 0 0