关于多线程编程
什么是多线程
简单的来说,并发应用程序中一个单独的执行路径,就是一个线程。
相关术语
- 进程:用于指代一个正在运行的可执行程序,它可以包含多个线程。
- 任务:用于指代抽象的概念,表示需要执行工作。
- 线程的同步:同步就是协同步调,按预定的先后次序进行运行。线程的同步即一个线程执行完指定操作后,另个一个线程再执行。
- 线程的互斥:线程互斥是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。
iOS中的多线程编程
- pthread(POSIX threads):一套C语言接口,定义了创建和管理线程的API,非常灵活,可以直接操作线程。
- NSThread:一套OC接口,需要自己手动管理线程,比GCD和NSOperation更加轻量级。
- GCD:是iOS4出的一套并发编程的接口,纯C语言接口。以队列为基础。
- NSOperation:一套面向对象的OC API,不需要手动管理线程。能实现一些GCD实现不了的功能。
NSThread
基本操作
线程的创建
类方法创建线程
1
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"helloworld"];
实例方法创建线程
1
2NSThread * th = [[NSThread alloc] initWithTarget:self selector:@selector(threadFun:) object:@"helloworld"];
[th start];继承NSThread,重写main方法(与NSOperation类似)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//MyThread.h
@interface MyThread : NSThread
@end
//MyThread.m
@implementation MyThread
- (void)main {
NSLog(@"this is MyThread");
}
//main.m
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyThread * th = [[MyThread alloc] init];
[th start];
[[NSRunLoop mainRunLoop] run];
}
return 0;
}
@end其他:NSObject中的方法
1
- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;
####线程的基本管理
- 获取主线程
+ (NSThread *)mainThread
- 获取当前线程
+ (NSThread *)currentThread;
- 阻塞当前线程
+ (void)sleepUntilDate:(NSDate *)date;
和+ (void)sleepForTimeInterval:(NSTimeInterval)ti;
- 销毁消除当前线程(currentThread)
+ (void)exit;
- 取消线程
- (void)cancel;
:将线程的状体置为cancelled,并不会停止线程的执行 - 线程键的通讯
1
2- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
线程同步与互斥
锁
@synchronized(obj)对代码段进行加锁,同时只能一个线程访问该代码段
1
2
3
4@synchronized(self) {
i++;
NSLog(@"%ld", i);
}NSLock:与@synchronized类似
1
2
3
4
5
6
7NSLock * lock = [[NSLock alloc] init];
if ([lock tryLock]) {
[lock lock];
i++;
NSLog(@"%ld", i);
[lock unlock];
}NSConditionLock条件锁:在所的基础上添加了条件,只有满足对应的条件,才能打开相应的锁
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- (void)startTest {
_conLock = [[NSConditionLock alloc] init];
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"top thread"];
for (int i = 0; i < 200; i++) {
[NSThread detachNewThreadSelector:@selector(threadFun2:) toTarget:self withObject:@(i)];
}
}
- (void)threadFun:(id)obj {
[_conLock lock];
NSLog(@"%@", obj);
[NSThread sleepForTimeInterval:1.0f];
//加锁,条件为10
[_conLock unlockWithCondition:10];
}
- (void)threadFun2:(id)obj {
NSInteger value = [obj integerValue];
//只有到value等于10时,才能成功加锁,否则阻塞
[_conLock lockWhenCondition:value];
NSLog(@"thread%@", obj);
[NSThread sleepForTimeInterval:1.0f];
//解锁,并把条件设为value + 3,则下次上锁的条件为13,一次类推13,16,19...
[_conLock unlockWithCondition:value + 3];
}
输出结果:
- NSRecursiveLock递归锁:同一个线程中,递归或多次调用加锁代码段,不会造成死锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17j = 0;
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"top thread1"];
[NSThread detachNewThreadSelector:@selector(threadFun:) toTarget:self withObject:@"top thread2"];
- (void)threadFun:(id)obj {
[self testWith:obj];
}
- (void)testWith:(id)obj {
[_recLock lock];
NSLog(@"test recursive,thread:%@, lock:%ld", obj, j++);
[NSThread sleepForTimeInterval:1.0f];
if (j < 10) {
[self testWith:obj];
}
[_recLock unlock];
}
输出结果:
- NSDistributedLock分布式锁:用于多个进程之间互斥访问资源,一般用于Mac开发
条件
使用NSCondition,可以实现多线程的同步,解决类似生产者消费者问题。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
@implementation testThread
{
NSInteger i;
NSCondition * _con;
NSMutableArray * _products;
}
- (void)startTest {
_con = [[NSCondition alloc] init];
_products = [[NSMutableArray alloc] init];
[NSThread detachNewThreadSelector:@selector(threadFun2:) toTarget:self withObject:@"thread2"];
[NSThread detachNewThreadSelector:@selector(threadFun2:) toTarget:self withObject:@"thread3"];
[NSThread detachNewThreadSelector:@selector(threadFun1:) toTarget:self withObject:@"thread1"];
}
- (void)threadFun1:(id)obj {
while (1) {
[NSThread sleepForTimeInterval:2.0f];
[_products addObject:@"apple"];//生产者创造一个产品
NSLog(@"%@ add one product, product count:%ld", obj, _products.count);
[_con signal];//通知消费者,消费产品
// [_con broadcast];
}
}
- (void)threadFun2:(id)obj {
while (1) {
[_con lock];
[_con wait];//等待生产者的通知
[_products removeObjectAtIndex:0];//消费者消费一个产品
NSLog(@"%@ remove one product, proudct count:%ld", obj, _products.count);
[_con unlock];
}
}
@end
GCD
基本用法
队列
- 串行队列:同时只能执行一个任务
- 所有任务都在同一个线程中执行,FIFO
- 多个串行队列之间是并行执行的,会占用多个线程
- 创建串行队列:
dispatch_queue_t serialQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_get_main_queue();
获取主线程上的一个全局可用的串行队列
- 并行队列:同时可以执行多个任务
同步任务:添加一个任务后,需要等待任务执行完后才能返回
- 无论是串行队列还是并行队列,同步任务都是在主线程上执行的
- 同步任务会阻塞当前线程
- 向主队列中添加同步任务会造成死锁,见死锁①
- 向串行队列中添加嵌套的同步任务会造成死锁,见死锁②
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23//同步任务
dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0f];
//先打印
NSLog(@"thread:%@", [NSThread currentThread]);
});
NSLog(@"haha");//后打印
//死锁①
dispatch_sync(dispatch_get_main_queue(), ^{
[NSThread sleepForTimeInterval:1.0f];
NSLog(@"thread:%@", [NSThread currentThread]);
});
NSLog(@"haha");//造成死锁,haha永远不会打印
//死锁②
dispatch_queue_t serialQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(serialQueue, ^{
NSLog(@"thread1:%@", [NSThread currentThread]);
dispatch_sync(serialQueue, ^{
NSLog(@"thread2:%@", [NSThread currentThread]);//造成死锁,thread2永远不会打印
});
});
异步任务:添加一个任务后不需要等待任务执行完成
1
2
3
4
5
6
7//异步任务
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[NSThread sleepForTimeInterval:1.0f];
//后打印
NSLog(@"thread:%@", [NSThread currentThread]);
});
NSLog(@"haha");//先打印
其他用法
dispatch_after:延迟指定的时间后,将任务添加到指定的队列中
1
2
3
4dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10.0f * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"haha");
});dispatch_group:将多个任务或队列归为一组,在这组的所有任务都执行完之后再执行后面的操作
- dispatch_group_async函数:创建一个指定队列中的异步任务,并将其添加到指定组中
- dispatch_group_notify函数:指定组中所有任务都执行完后,会执行该函数所指定的的任务
- dispatch_group_wait函数:阻塞当前线程,等待group中所有任务执行完毕
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//使用dispatch_group_notify
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"1");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"2");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"3");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"4");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_notify(group, concurrentQueue, ^{
NSLog(@"5");//"1234"全部打印后,才会打印5
});
//使用dispatch_group_wait
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t concurrentQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"1");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"2");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"3");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_async(group, concurrentQueue, ^{
NSLog(@"4");
[NSThread sleepForTimeInterval:1.0f];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"haha");//"dispatch_group_wait"会阻塞线程,所有haha会在"1234"之后打印
diapatch_once:执行一次
dispatch_apply:按照指定的次数将指定的Block追加到指定的DispatchQueue中,并等待全部处理执行结束
1
2
3
4
5dispatch_apply(10, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t t) {
NSLog(@"%zu", t);
[NSThread sleepForTimeInterval:1.0f];
});
NSLog(@"haha");//等待dispatch_apply中的所有任务都执行完之后才会带引"haha"dispatch_suspend / dispatch_resume
- dispatch_suspend:挂起指定的队列
- dispatch_resume:恢复指定的队列
- Dispatch Semaphore:信号量,用于排他控制
1
2
3
4
5
6
7
8
9
10
11
12
13dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
for (int i = 0; i < 10; i++) {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
long result = dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
//排他,同时只能有一个线程进入这个代码块,相当于锁
NSLog(@"end wait, result:%ld", result);
[NSThread sleepForTimeInterval:1.0f];
dispatch_semaphore_signal(semaphore);
});
}
NSOperation
基本用法
NSBlockOperation:以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
29
30
31
32
33
34
35
36
37@interface testOperation ()
@property (nonatomic, strong) NSOperationQueue * queue;
@end
@implementation testOperation
- (void)startTest {
//创建操作
NSOperation * oper1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"test block operation1, current thread:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0f];
}];
NSOperation * oper2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"test block operation2, current thread:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0f];
}];
NSOperation * oper3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"test block operation3, current thread:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0f];
}];
NSOperation * oper4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"test block operation4, current thread:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0f];
}];
self.queue = [[NSOperationQueue alloc] init];
//将操作加入队列中,操作就会在队列中并发执行,NSOperationQueue默认是并发队列
[self.queue addOperation:oper1];
[self.queue addOperation:oper2];
[self.queue addOperation:oper3];
[self.queue addOperation:oper4];
}
@endNSInvocationOperation
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@interface testOperation ()
@property (nonatomic, strong) NSOperationQueue * queue;
@end
@implementation testOperation
- (void)startTest {
NSMethodSignature * sig = [[self class] instanceMethodSignatureForSelector:@selector(oper1Fun:andObj2:)];
NSInvocation * invo = [NSInvocation invocationWithMethodSignature:sig];
[invo setTarget:self];
[invo setSelector:@selector(oper1Fun:andObj2:)];
NSString * arg1 = @"haha";
NSString * arg2 = @"oooo";
[invo setArgument:&arg1 atIndex:2];
[invo setArgument:&arg2 atIndex:3];
NSOperation * oper1 = [[NSInvocationOperation alloc] initWithInvocation:invo];
NSOperation * oper2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(oper2Fun) object:nil];
self.queue = [[NSOperationQueue alloc] init];
[self.queue addOperation:oper1];
[self.queue addOperation:oper2];
}
- (void)oper1Fun:(id)obj1 andObj2:(id)obj2 {
NSLog(@"test invocation operation1, current thread:%@, obj1:%@, obj2:%@", [NSThread currentThread], obj1, obj2);
[NSThread sleepForTimeInterval:1.0f];
}
- (void)oper2Fun {
NSLog(@"test invocation operation2, current thread:%@", [NSThread currentThread]);
}
@end继承NSOperation,重写main方法
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@interface MyOperation : NSOperation
@end
@implementation MyOperation
- (void)main {
NSLog(@"MyOperation, current thread:%@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0f];
}
@end
//main.m
NSOperationQueue * queue = [[NSOperationQueue alloc] init];
MyOperation * op1 = [[MyOperation alloc] init];
MyOperation * op2 = [[MyOperation alloc] init];
MyOperation * op3 = [[MyOperation alloc] init];
MyOperation * op4 = [[MyOperation alloc] init];
MyOperation * op5 = [[MyOperation alloc] init];
MyOperation * op6 = [[MyOperation alloc] init];
[queue addOperation:op1];
[queue addOperation:op2];
[queue addOperation:op3];
[queue addOperation:op4];
[queue addOperation:op5];
[queue addOperation:op6];
其他用法
- 设置最大并发数
self.queue.maxConcurrentOperationCount = 3;
- 设置操作之间的依赖关系
[oper2 addDependency:oper1];//oper2依赖于oper1,即必须oper1执行完之后才能执行oper2
- 手动管理NSOperation
- 运行一个Operation:
- (void)start;
默认的start方法会直接进行一些异常判断,然后直接调用- (void)main;
- 取消一个Operation:
- (void)cancel;
调用cancel方法并不会停止操作的执行,这取决于main方法中对cancel的处理。如果在main方法中没有对cancel进行处理,发送cancel消息是没有任何效果的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//MyOperation.m
@implementation MyOperation
- (void)start {
[NSThread detachNewThreadSelector:@selector(main) toTarget:self withObject:nil];
}
- (void)main {
NSInteger index = 0;
while (1) {
NSLog(@"current thread:%@, index:%ld", [NSThread currentThread], index++);
if (self.isCancelled) {
NSLog(@"cancel");
return;
}
[NSThread sleepForTimeInterval:1.0f];
}
}
@end
//main.m
MyOperation * op1 = [[MyOperation alloc] init];
[op1 start];
[NSThread sleepForTimeInterval:3.0f];
[op1 cancel];
- 运行一个Operation: