GCD多线程用法总结

在iOS开发中GCD开启线程相对于NSOperation来说更加方便顺手。GCD通过队列来保存需要执行的的任务,执行函数来执行任务。队列的特征是FIFO(先入先出),队列中主要存放任务最后交给线程来执行,在GCD的所有类型的队列中的任务都遵守这个规则。GCD是一个中央调度器或者任务派发器,语法结构是通过C语言构成的,面向过程的开发。

进程:一个应用只有一个进程,进程在应用第一次执行的创建,进程主要为应用在内存开辟一块空间。每个进程是独立,每个进程开辟的内存空间是受保护 的,只能由自己访问。

线程:一个应用拥有多条线程,线程主要用来执行应用程序中的代码(任务)。iOS开发中最佳开启线程的条数3~6条。
一条线程同时只能执行一个任务,有多个任务时按顺序执行。
线程是进程的基本执行单元,一个进程的所有任务都在线程中执行。
线程间可以共享内存空间和数据。

多线程:
一个进程开辟多条线程,每条线程可以同时(并发)执行任务
多线程可以提高程序执行的效率

多线程原理:
一个CPU在同一时间只能执行一条指令,即同一时间只能执行一个任务;但是CPU的执行速度特别快,当有多条线程时,CPU快速地在多条线程间来回切换,如果线程间调度的速度足够快,就会造成CPU同时执行多个任务的假象。在多线程当中,线程数不应该太多,否则每条线程的执行频率会降低,严重影响系统性能。

多线程优缺点:
优点:提高程序的执行效率,适当提高内存的利用率
缺点:开启线程需要消耗内存空间以前主线程1MB,目前主线程和子线程都是512KB内存空间,开启过多的线程会占用内存空间,降低性能。线程越多,CPU的在每条线程的调度频率降低。程序的复杂度提高。

多线程使用核心:更新UI在主线程进行,主线程即UI线程;耗时操作在子线程执行。

GCD中的4种队列

  1. 主队列:保存用来在主线程执行的任务。(MainQueue) 2. 串行队列:存放需要按顺序执行的任务。(SerialQueue)
  2. 并发队列:存放需要同时执行的任务,如果有多条线程,任务没有执行顺序。(ConcurrentQueue)
  3. 全局并发队列:特征和并发队列相同,是系统创建好的的队列,不要要创建,获取就行。(GlobalQueue)

GCD中的2种最主要的执行函数:
1. 同步执行函数:没有开启新的线程的能力,任务在当前线程执行,任务按顺序执行。(Synchronize |ˈsɪŋkrənaɪz|)

  1. 异步执行函数:内部封装了开启线程的能力,开启线程的条数不确定,多条线程的话,任务没有执行顺序。(Asynchronous |eɪˈsɪŋkrənəs|)

GCD中4种队列和2中执行函数的混合使用情况:

  1. 串行队列 + 同步执行函数
    效果:任务按顺序执行,并且在当前线程执行
    结论:同步执行函数没有开启线程的能力
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    NSLog(@"start--%@",[NSThread currentThread]);

    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

    dispatch_sync(queue, ^{

    NSLog(@"任务1--%@",[NSThread currentThread]);
    });

    dispatch_sync(queue, ^{

    NSLog(@"任务2--%@",[NSThread currentThread]);
    });

    dispatch_sync(queue, ^{

    NSLog(@"任务3--%@",[NSThread currentThread]);
    });

    NSLog(@"end --- %@",[NSThread currentThread]);

运行效果:

2015-12-28 11:11:10.032 04.GCD队列和函数的基本用法[1118:67741] start–{number = 1, name = main}
2015-12-28 11:11:10.032 04.GCD队列和函数的基本用法[1118:67741] 任务1–{number = 1, name = main}
2015-12-28 11:11:10.033 04.GCD队列和函数的基本用法[1118:67741] 任务2–{number = 1, name = main}
2015-12-28 11:11:10.033 04.GCD队列和函数的基本用法[1118:67741] 任务3–{number = 1, name = main}
2015-12-28 11:11:10.033 04.GCD队列和函数的基本用法[1118:67741] end — {number = 1, name = main}

  1. 串行队列 + 异步执行函数
    效果:任务在子线程执行,创建一条线程,任务按顺序执行,任务等待主线程任务执行完毕之后执行
    结论:异步函数具有创建线程的能力,串行队列要求任务按顺序执行,异步函数会等待主线程任务执行完毕
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    NSLog(@"start--%@",[NSThread currentThread]);

    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL);

    dispatch_async(queue, ^{

    NSLog(@"任务1--%@",[NSThread currentThread]);
    });

    dispatch_async(queue, ^{

    NSLog(@"任务2--%@",[NSThread currentThread]);
    });

    dispatch_async(queue, ^{

    NSLog(@"任务3--%@",[NSThread currentThread]);
    });

    NSLog(@"end --- %@",[NSThread currentThread]);

运行效果:

2015-12-28 11:19:29.807 04.GCD队列和函数的基本用法[1167:73242] start–{number = 1, name = main}
2015-12-28 11:19:29.808 04.GCD队列和函数的基本用法[1167:73242] end — {number = 1, name = main}
2015-12-28 11:19:29.808 04.GCD队列和函数的基本用法[1167:73286] 任务1–{number = 2, name = (null)}
2015-12-28 11:19:29.808 04.GCD队列和函数的基本用法[1167:73286] 任务2–{number = 2, name = (null)}
2015-12-28 11:19:29.809 04.GCD队列和函数的基本用法[1167:73286] 任务3–{number = 2, name = (null)}

  1. 并发队列 + 同步执行函数
    效果:任务在当前线程执行,任务按顺序执行
    结论:同步函数没有开启线程的能力,并发队列允许多个任务同时执行
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    NSLog(@"start--%@",[NSThread currentThread]);

    dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);

    dispatch_sync(queue, ^{

    NSLog(@"任务1--%@",[NSThread currentThread]);
    });

    dispatch_sync(queue, ^{

    NSLog(@"任务2--%@",[NSThread currentThread]);
    });

    dispatch_sync(queue, ^{

    NSLog(@"任务3--%@",[NSThread currentThread]);
    });

    NSLog(@"end --- %@",[NSThread currentThread]);

运行效果:

2015-12-28 11:20:07.132 04.GCD队列和函数的基本用法[1182:73808] start–{number = 1, name = main}
2015-12-28 11:20:07.133 04.GCD队列和函数的基本用法[1182:73808] 任务1–{number = 1, name = main}
2015-12-28 11:20:07.133 04.GCD队列和函数的基本用法[1182:73808] 任务2–{number = 1, name = main}
2015-12-28 11:20:07.133 04.GCD队列和函数的基本用法[1182:73808] 任务3–{number = 1, name = main}
2015-12-28 11:20:07.133 04.GCD队列和函数的基本用法[1182:73808] end — {number = 1, name = main}

  1. 并发队列 + 异步执行函数
    效果:开启多条线程,任务会等待主线程任务执行完毕,子线程任务执行没有顺序
    结论:异步执行函数具有开启线程能力,线程开启的条数内部自动控制,会等待主线程任务执行结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NSLog(@"start--%@",[NSThread currentThread]);

dispatch_queue_t queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);

dispatch_async(queue, ^{

NSLog(@"任务1--%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{

NSLog(@"任务2--%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{

NSLog(@"任务3--%@",[NSThread currentThread]);
});

NSLog(@"end --- %@",[NSThread currentThread]);

运行效果:

2015-12-28 11:20:36.259 04.GCD队列和函数的基本用法[1195:74285] start–{number = 1, name = main}
2015-12-28 11:20:36.259 04.GCD队列和函数的基本用法[1195:74323] 任务1–{number = 2, name = (null)}
2015-12-28 11:20:36.259 04.GCD队列和函数的基本用法[1195:74285] end — {number = 1, name = main}
2015-12-28 11:20:36.260 04.GCD队列和函数的基本用法[1195:74322] 任务2–{number = 3, name = (null)}
2015-12-28 11:20:36.260 04.GCD队列和函数的基本用法[1195:74325] 任务3–{number = 4, name = (null)}

  1. 主队列 + 同步执行函数
    效果:任务在主线程执行,开始执行主队列的任务时会卡死主线程
    结论:主队列的任务在主线程执行,卡死主线程原因:(主线程在执行主队列中的同步任务,主队列中的同步任务在主线程执行)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NSLog(@"start--%@",[NSThread currentThread]);

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_sync(queue, ^{

NSLog(@"任务1--%@",[NSThread currentThread]);
});

dispatch_sync(queue, ^{

NSLog(@"任务2--%@",[NSThread currentThread]);
});

dispatch_sync(queue, ^{

NSLog(@"任务3--%@",[NSThread currentThread]);
});

NSLog(@"end --- %@",[NSThread currentThread]);

运行效果:

2015-12-28 11:21:11.687 04.GCD队列和函数的基本用法[1212:74885] start–{number = 1, name = main}

  1. 主队列 + 异步执行函数
    效果:任务在主线程执行,等待主线程任务执行完毕
    结论:主队列任务在主线程执行,异步函数会等待当前线程任务执行结束
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NSLog(@"start--%@",[NSThread currentThread]);

dispatch_queue_t queue = dispatch_get_main_queue();

dispatch_async(queue, ^{

NSLog(@"任务1--%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{

NSLog(@"任务2--%@",[NSThread currentThread]);
});

dispatch_async(queue, ^{

NSLog(@"任务3--%@",[NSThread currentThread]);
});

NSLog(@"end --- %@",[NSThread currentThread]);

运行效果:

2015-12-28 11:21:39.205 04.GCD队列和函数的基本用法[1222:75354] start–{number = 1, name = main}
2015-12-28 11:21:39.206 04.GCD队列和函数的基本用法[1222:75354] end — {number = 1, name = main}
2015-12-28 11:21:39.206 04.GCD队列和函数的基本用法[1222:75354] 任务1–{number = 1, name = main}
2015-12-28 11:21:39.206 04.GCD队列和函数的基本用法[1222:75354] 任务2–{number = 1, name = main}
2015-12-28 11:21:39.206 04.GCD队列和函数的基本用法[1222:75354] 任务3–{number = 1, name = main}

  1. 队列嵌套使用
    要求的效果:第一和第二个任务在第二个子线程按顺序执行,第三、四个任务等待前两个任务执行结束后在主线程执行,第五个任务在三四任务前按顺序执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

NSLog(@"start");

dispatch_async(dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL), ^{

NSLog(@"任务1-%@",[NSThread currentThread]);

dispatch_sync(dispatch_queue_create(NULL, DISPATCH_QUEUE_SERIAL), ^{

NSLog(@"任务2-%@",[NSThread currentThread]);
});

dispatch_async(dispatch_get_global_queue(0, 0), ^{

NSLog(@"任务3-%@",[NSThread currentThread]);
});

dispatch_async(dispatch_get_global_queue(0, 0), ^{

NSLog(@"任务4-%@",[NSThread currentThread]);
});

NSLog(@"任务5-%@",[NSThread currentThread]);

});

NSLog(@"end");

运行效果:

2015-12-28 11:22:02.043 04.GCD队列和函数的基本用法[1232:75748] start
2015-12-28 11:22:02.043 04.GCD队列和函数的基本用法[1232:75748] end
2015-12-28 11:22:02.043 04.GCD队列和函数的基本用法[1232:75785] 任务1-{number = 2, name = (null)}
2015-12-28 11:22:02.044 04.GCD队列和函数的基本用法[1232:75785] 任务2-{number = 2, name = (null)}
2015-12-28 11:22:02.044 04.GCD队列和函数的基本用法[1232:75785] 任务5-{number = 2, name = (null)}
2015-12-28 11:22:02.044 04.GCD队列和函数的基本用法[1232:75786] 任务3-{number = 3, name = (null)}
2015-12-28 11:22:02.044 04.GCD队列和函数的基本用法[1232:75788] 任务4-{number = 4, name = (null)}


GCD其它用法:

  1. 延时操作
1
2
3
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
<#code to be executed after a specified delay#>
});
  1. 一次性代码:常用语创建一个单例
1
2
3
4
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
<#code to be executed once#>
});