iOS多线程

多线程

多线程(面试题)汇总

iOS多线程有四套多线程方案:

  • Pthreads
  • NSThread
  • GCD
  • NSOperation & NSOperationQueue

dispatch_group

NSThread

不常用,主要用到[NSThread currentThread]函数来获取当前线程

GCD (任务和队列)

如果dispatch_group_async里执行的是异步代码dispatch_group_notify会直接触发而不会等待异步任务完成,而dispatch_group_enter、和dispatch_group_leave则不会有这个问题,它们只需要在任务开始前enter结束后leave即可达到线程同步的效果。

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
28
29
30
dispatch_queue_t dispatchQueue = dispatch_queue_create("ted.queue.next1", DISPATCH_QUEUE_CONCURRENT);
dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
dispatch_group_t dispatchGroup = dispatch_group_create();
dispatch_group_enter(dispatchGroup);
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_async(globalQueue, ^{
sleep(5);
NSLog(@"任务一完成");
dispatch_group_leave(dispatchGroup);
});
});
dispatch_group_enter(dispatchGroup);
dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
dispatch_async(globalQueue, ^{
sleep(8);
NSLog(@"任务二完成");
dispatch_group_leave(dispatchGroup);
});
});
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
NSLog(@"notify:任务都完成了");
});
2018-08-21 21:38:20.803437+0800 Demo[19819:13724794] 任务一完成
2018-08-21 21:38:23.800334+0800 Demo[19819:13724792] 任务二完成
2018-08-21 21:38:23.800677+0800 Demo[19819:13724748] notify:任务都完成了

如果block块里面执行的是同步代码,则以用dispatch_group_enter dispatch_group_leavedispatch_group_async实现效果一样

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
28
29
30
31
32
33
34
35
36
37
方式一:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"task A");
sleep(5);
NSLog(@"task a done");
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
NSLog(@"task B");
sleep(3);
NSLog(@"task b done");
dispatch_group_leave(group);
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});
方式二:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
NSLog(@"task A");
sleep(5);
NSLog(@"task a done");
});
dispatch_group_async(group, queue, ^{
NSLog(@"task B");
sleep(3);
NSLog(@"task b done");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"done");
});

串行队列

  1. 同步串行,不会创建新线程,各个任务在主线程中,依次执行
  2. 异步串行,只创建一次新线程,各个任务依次执行
    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
    28
    29
    // create queue
    dispatch_queue_t queue = dispatch_queue_create("com.example.gcd.queuename", DISPATCH_QUEUE_SERIAL); //DISPATCH_QUEUE_CONCURRENT
    // 串行同步不会创建线程
    dispatch_sync(queue, ^{
    NSLog(@"serial queue sync task1 --%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
    NSLog(@"serial queue sync task2 --%@", [NSThread currentThread]);
    });
    // 串行异步会创建线程,当前队列中第一次异步会创建线程之后不会在重新创建
    dispatch_async(queue, ^{
    NSLog(@"serial queue async task1 --%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"serial queue async task2 --%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
    NSLog(@"serial queue sync task3 --%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"serial queue async task3 --%@", [NSThread currentThread]);
    });
    serial queue sync task1 --<NSThread: 0x600000069480>{number = 1, name = main}
    serial queue sync task2 --<NSThread: 0x600000069480>{number = 1, name = main}
    serial queue async task1 --<NSThread: 0x60000066cdc0>{number = 6, name = (null)}
    serial queue async task2 --<NSThread: 0x60000066cdc0>{number = 6, name = (null)}
    serial queue sync task3 --<NSThread: 0x600000069480>{number = 1, name = main}
    serial queue async task3 --<NSThread: 0x60000066cdc0>{number = 6, name = (null)}

并发队列

  1. 同步并发,不会创建新线程,各个任务在主线程中,依次执行
  2. 异步并发,每async一次创建一次新线程,任务的执行顺序会根据处理内容和系统状态发生改变

NSOperation & NSOperationQueue

NSOperation是面向对象的。是在GCD基础上封装的,NSOperation和 NSOperationQueue分别对应GCD的任务和队列,可以设置最大并发量,task之间的依赖关系等

一般不直接用NSOperationQueue,用它的几个子类来操作,否者需要自己管理线程

练习

  1. 子线程同时执行ABC三个同步任务、全部执行完成再在子线程执行三个同步任务EDF
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    方式一: group
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_queue_create("com.emoney.cn", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_async(group, queue, ^{
    NSLog(@"task A");
    });
    dispatch_group_async(group, queue, ^{
    NSLog(@"task B");
    });
    dispatch_group_async(group, queue, ^{
    NSLog(@"task C");
    });
    dispatch_group_notify(group, queue, ^{
    dispatch_sync(queue, ^{
    NSLog(@"task D, thread:%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
    NSLog(@"task E");
    });
    dispatch_sync(queue, ^{
    NSLog(@"task F");
    });
    });
    方式二:dispatch_barrier_async
    The queue you specify should be a concurrent queue that you create yourself using the dispatch_queue_create function. If the queue you pass to this function is a serial queue or one of the global concurrent queues, this function behaves like the dispatch_sync function.
    官方说明大意:在使用栅栏函数时.使用自定义队列才有意义,如果用的是串行队列或者系统提供的全局并发队列,这个栅栏函数的作用等同于一个同步函数的作用
    dispatch_queue_t queue = dispatch_queue_create("com.emoney.cn", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
    NSLog(@"task A, thread:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"task B, thread:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"task C, thread:%@", [NSThread currentThread]);
    });
    NSLog(@"ABC");
    dispatch_barrier_async(queue, ^{
    NSLog(@"task ABC Done");
    });
    dispatch_async(queue, ^{
    NSLog(@"task D, thread:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"task E, thread:%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
    NSLog(@"task F, thread:%@", [NSThread currentThread]);
    });
    NSLog(@"DEF");
    方式三:NSOperationQueue dependency
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operationA = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task A, thread:%@", [NSThread currentThread]);
    }];
    NSBlockOperation *operationB = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task B, thread:%@", [NSThread currentThread]);
    }];
    NSBlockOperation *operationC = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task C, thread:%@", [NSThread currentThread]);
    }];
    NSBlockOperation *operationD = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task D, thread:%@", [NSThread currentThread]);
    }];
    NSBlockOperation *operationE = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task E, thread:%@", [NSThread currentThread]);
    }];
    NSBlockOperation *operationF = [NSBlockOperation blockOperationWithBlock:^{
    NSLog(@"task F, thread:%@", [NSThread currentThread]);
    }];
    [operationD addDependency:operationA];
    [operationD addDependency:operationB];
    [operationD addDependency:operationC];
    [operationE addDependency:operationA];
    [operationE addDependency:operationB];
    [operationE addDependency:operationC];
    [operationF addDependency:operationA];
    [operationF addDependency:operationB];
    [operationF addDependency:operationC];
    [queue addOperation:operationA];
    [queue addOperation:operationB];
    [queue addOperation:operationC];
    [queue addOperation:operationD];
    [queue addOperation:operationE];
    [queue addOperation:operationF];

dispatch_semaphore(信号量)的理解及使用

问题描述:

假设现在系统有两个空闲资源可以被利用,但同一时间却有三个线程要进行访问,这种情况下,该如何处理呢?

或者

我们要下载很多图片,并发异步进行,每个下载都会开辟一个新线程,可是我们又担心太多线程肯定cpu吃不消,那么我们这里也可以用信号量控制一下最大开辟线程数。

定义:

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
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task 1");
sleep(1);
NSLog(@"complete task 1");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task 3");
sleep(1);
NSLog(@"complete task 3");
dispatch_semaphore_signal(semaphore);
});