iOS多线程主要有四种实现方式

Pthread

pthread是c语言的底层库对多线程调用的方式,具有跨平台的能力,但是一般在OC的编程中不使用。

NSThread

NSThread是对Pthread的一层封装,但是仍旧需要手动对线程进行管理,实际使用过程中可能造成线程的大量创建从而浪费内存。

NSThread的常用方法有:

创建线程

1
2
3
4
5
6
// 需要显式掉用start函数
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
- (instancetype)initWithBlock:(void (^)(void))block;
// 不需要显式调用start函数
+ (void)detachNewThreadWithBlock:(void (^)(void))block;
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

启动线程

1
[thread start];

获取线程

1
2
[NSThread mainThread];
[NSThread currentThread];

退出与取消线程

1
2
[thread exit];
[thread cancel];

判断当前线程是否为主线程

1
[thread isMainThread];

睡眠

1
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

GCD - Grand Central Dispatch

GCD会自动利用CPU的多核,并自动管理线程的生命周期,主要用法如下:

创建队列

1
2
dispatch_queue_create(const char *_Nullable label,
dispatch_queue_attr_t _Nullable attr);

第一个参数表示该队列的名字,用于标识队列,第二个参数用于设置队列的属性,常见属性有:

DISPATCH_QUEUE_SERIAL - 队列内的所有操作串行进行;

DISPATCH_QUEUE_SERIAL_INACTIVE - 队列内操作串行,初始为不活跃状态,需dispatch_activate激活;

DISPATCH_QUEUE_CONCURRENT - 队列内操作串行;

DISPATCH_QUEUE_CONCURRENT_INACTIVE - 队列内操作串行,但是使用时需要激活;

获取内置队列

获取主线程所在队列

1
dispatch_queue_main_t dispatch_get_main_queue(void);

获取其他内置队列

1
dispatch_queue_global_t dispatch_get_global_queue(long identifier, unsigned long flags);

第一个参数表示队列的优先级,一般使用DISPATCH_QUEUE_PRIORITY_DEFAULT

DISPATCH_QUEUE_PRIORITY_HIGH,DISPATCH_QUEUE_PRIORITY_DEFAULT,DISPATCH_QUEUE_PRIORITY_LOW,DISPATCH_QUEUE_PRIORITY_BACKGROUND

第二个参数为预留参数,设为0即可。

同步

1
void dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);

举例:

1
2
3
4
5
dispatch_queue_t queue_sync = dispatch_queue_create("SyncQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue_sync, ^{
sleep(2);
NSLog(@"Sync");
});

注意当这段代码是在主线程内执行的时候,这里的queue不能是主线程所在的队列,如果是主线程的话,可能会造成死锁,因此一般不使用这个方法。

异步

1
void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

举例:

1
2
3
4
dispatch_async(queue, ^{
sleep(2);
NSLog(@"1");
});

延迟操作

1
void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block);

举例:

1
2
3
4
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"wait");
});

组操作

主要是可以使用dispatch_group_notify在完成一组任务之后进行一定的操作。

举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("groupQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue, ^{
sleep(2);
NSLog(@"1");
});
dispatch_group_async(group, queue, ^{
sleep(2);
NSLog(@"2");
});
dispatch_group_async(group, queue, ^{
sleep(3);
[NSThread sleepForTimeInterval:3];
NSLog(@"3");
});
dispatch_group_notify(group, queue, ^{
NSLog(@"eee");
});

栅栏

1
void dispatch_barrier_sync(dispatch_queue_t queue, dispatch_block_t block);

举例:

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
- (void)viewDidLoad {
[super viewDidLoad];
[self useGCDGroup];
NSLog(@"ViewDidLoad");
}
- (void) useGCDGroup
{
dispatch_queue_t queue = dispatch_queue_create("groupQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
sleep(2);
NSLog(@"1");
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"2");
});
dispatch_barrier_sync(queue, ^{
NSLog(@"barrier");
});
dispatch_async(queue, ^{
sleep(4);
NSLog(@"3");
});
dispatch_async(queue, ^{
sleep(2);
NSLog(@"4");
});
}

barrier所对应的任务之前的任务都完成之后才会进行barrier的任务,然后才会进行barrier之后的任务。上述例子的输出结果为:

1
2
3
4
5
6
1
2
barrrier
ViewDidLoad
4
3

其中1和2,3和4均有可能互换。

单例

用于保证block内代码只执行一次。

举例

1
2
3
4
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
NSLog(@"dispatch");
});

信号量

1
2
3
4
5
6
7
typedef NSObject<OS_dispatch_semaphore> *dispatch_semaphore_t;
// 声明信号量
dispatch_semaphore_t dispatch_semaphore_create(long value);
// 信号量 - 1, 相当于持有信号量
long dispatch_semaphore_wait(dispatch_semaphore_t dsema, dispatch_time_t timeout);
// 信号量 + 1, 相当于释放信号量
long dispatch_semaphore_signal(dispatch_semaphore_t dsema);

一般dispatch_semaphore_wait和dispatch_semaphore_signal成对使用,创建信号量时的参数即代表资源的持有量,如有多少个可用线程等。

举例:

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 queue = dispatch_queue_create("useSemphore", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
__block NSString *result = @"";
dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task 1 begins %@", [NSThread currentThread]);
sleep(2);
result = [result stringByAppendingFormat:@"ssssss"];
NSLog(@"task 1 ends");
dispatch_semaphore_signal(semaphore);
});

dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"task 2 begins %@", [NSThread currentThread]);
sleep(2);
result = [result stringByAppendingFormat:@"eeeeee"];
NSLog(@"task 2 ends");
dispatch_semaphore_signal(semaphore);
});
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"result = %@", result);
dispatch_semaphore_signal(semaphore);
dispatch_semaphore_signal(semaphore);
});

});

NSOperation

NSOperation是对GCD的又一层封装,一般有四种使用方式

NSInvocationOperation

1
2
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
[op start];

NSBlockOperation

1
2
3
4
5
6
7
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
[op addExecutionBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
[op start];

注意这里在初始化的时候定义的操作是在主线程上进行的,而addExecutionBlock时加的操作不是在主线程上进行的。

自定义NSOperation的子类

通过重写一些方法如main,来实现自定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@interface Wrapper : NSOperation
@end
@implementation Wrapper
- (void)main
{
if (!self.isCancelled) {
for (int i = 0; i < 3; i++) {
sleep(2);
NSLog(@"1 %@", [NSThread currentThread]);
}
}
}
@end
// 使用
Wrapper *op = [[Wrapper alloc] init];
[op start];

NSOperationQueue

通过定义多个NSInvocationOperation和NSBlockOperation对象,并将它们放进一个NSOperationQueue类型的对象中,实现整个队列的统筹。同时,可以通过addDependency等方法实现依赖的先后操作顺序。

举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 3; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"2 ---%@", [NSThread currentThread]);
}
}];
[op2 addExecutionBlock:^{
for (int i = 0; i < 3; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"3 ---%@", [NSThread currentThread]);
}
}];
NSInvocationOperation *op3 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
[op1 addDependency:op3];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
queue.maxConcurrentOperationCount = 1; // 设置最大并发操作数