iOS 线程详解,彻底学会线程

xiaoxiao2021-02-28  57

有人问我线程的问题,我简单总结一下。 要知其然,更要知其所以然

首先,什么是线程?

一个运行着的程序就是一个进程或者叫做一个任务,一个进程至少包含一个线程,线程就是程序的执行流。 iOS中的程序启动,创建好一个进程的同时,一个线程便开始运作,这个线程叫做主线程。主线成在程序中的位置和其他线程不同,它是其他线程最终的父线程,且所有的界面的显示操作即AppKit或UIKit的操作必须在主线程进行。 系统中每一个进程都有自己独立的虚拟内存空间,而同一个进程中的多个线程则公用进程的内存空间。 每创建一个新的进成,都需要一些内存(如每个线程有自己的stack空间)和消耗一定的CPU时间。 当多个进成对同一个资源出现争夺的时候需要注意线程安全问题

网络传输方式 a:同步:所有任务放在一个线程中完成,只要当前的任务没有完成 那么下一个任务就处于阻塞的状态 b:异步:多个任务放在多个线程中完成 即使当前任务没有完成 也不会永祥其他任务的执行

数据传输方式 a: 串行:一个线程中只有当前任务完成以后下一个任务才能执行 b: 并发:多个线程完成不同的任务 互不干扰

1.NSThread

//只需要创建线程 不需要手动开启线程 //线程对象只要被创建 就自动开始执行任务 [NSThread detachNewThreadSelector:@selector(threadMain1:) toTarget:self withObject:num];

或者

NSNumber *num = [NSNumber numberWithInt:100]; //创建线程 //2。 NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(threadMain2:) object:num]; //必须手动开启线程 [thread start];

//只要通知结束就会调用threadExit:这个方法 这个方法会被调用(有几个线程,调用几次)

[[NSNotificationCenter defaultCenter]addObserver:self selector:@selector(threadExit:) name:NSThreadWillExitNotification object:nil];

// UI刷新 [self performSelectorOnMainThread:@selector(refreshUI) withObject:nil waitUntilDone:NO];

注释:资源争夺的时候,比如卖火车票,不能一张卖多人,就需要加锁 lock 完事了unlock

2.NSOperation

2.1.创建队列的对象 NSOperationQueue *queue = [[NSOperationQueue alloc]init]; 2. 2.设置队列中盛放线程的最大个数 [queue setMaxConcurrentOperationCount:4]; 2. 3.队列中得优先级都是平等的

//(1)创建单独子线程 NSInvocationOperation *test1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(threadMain1:) object:nil]; //线程执行结束以后触发先面对而方法 [test1 setCompletionBlock:^{ NSLog(@"text1线程执行结束"); }]; //(2).创建block类型的线程 NSBlockOperation *test2 = [NSBlockOperation blockOperationWithBlock:^{ for (int i = 0; i < 100; i++) { NSLog(@"test2 i=%d",i); [NSThread sleepForTimeInterval:0.1]; } }]; [test2 setCompletionBlock:^{ NSLog(@"test2线程执行结束"); //线程执行结束以后 会自动从队列中移除并且退出 }]; [queue addOperation:test2];

2.4.将子线程放在队列中 [queue addOperation:test1]; //子线程放入队列中 就会立即执行线程方法

3.GCD

GCD是基于C语言底层API实现的一套多线程并发机制,非常的灵活方便,在实际的开发中使用很广泛。 简单来说CGD就是把 操作 放在 队列 中去执行。 自动管理线程的生命周期,自动利用更多的CPU内核 3.1. 队列的创建方法

可以使用dispatch_queue_create来创建对象,需要传入两个参数,第一个参数表示队列的唯一标识符,用于DEBUG,可为空;第二个参数用来识别是串行队列还是并发队列。DISPATCH_QUEUE_SERIAL表示串行队列,DISPATCH_QUEUE_CONCURRENT表示并发队列。 // 串行队列的创建方法 dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); // 并发队列的创建方法 dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT); 对于并发队列,还可以使用dispatch_get_global_queue来创建全局并发队列。GCD默认提供了全局的并发队列,需要传入两个参数。第一个参数表示队列优先级,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT。第二个参数暂时没用,用0即可。 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) // 同步执行任务创建方法 dispatch_sync(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码 }); // 异步执行任务创建方法 dispatch_async(queue, ^{ NSLog(@"%@",[NSThread currentThread]); // 这里放任务代码 });

各两种,所以就有四种情况了

1.并发队列 + 同步执行

不会开启新线程,执行完一个任务,再执行下一个任务

//并发队列 + 同步执行 -(void)GCD1{ NSLog(@"syncConcurrent---begin"); dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1------%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2------%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"3------%@",[NSThread currentThread]); } }); NSLog(@"syncConcurrent---end"); }

输出

syncConcurrent---begin 1------<NSThread: 0x60000007f200>{number = 1, name = main} 1------<NSThread: 0x60000007f200>{number = 1, name = main} 2------<NSThread: 0x60000007f200>{number = 1, name = main} 2------<NSThread: 0x60000007f200>{number = 1, name = main} 3------<NSThread: 0x60000007f200>{number = 1, name = main} 3------<NSThread: 0x60000007f200>{number = 1, name = main} syncConcurrent---end

2. 并发队列 + 异步执行

可同时开启多线程,任务交替执行 NSLog(@"asyncConcurrent---begin"); dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1------%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2------%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"3------%@",[NSThread currentThread]); } }); NSLog(@"asyncConcurrent---end");

输出

asyncConcurrent---begin asyncConcurrent---end 1------<NSThread: 0x60000007e680>{number = 3, name = (null)} 2------<NSThread: 0x608000079fc0>{number = 4, name = (null)} 3------<NSThread: 0x60800007b140>{number = 5, name = (null)} 1------<NSThread: 0x60000007e680>{number = 3, name = (null)} 2------<NSThread: 0x608000079fc0>{number = 4, name = (null)} 3------<NSThread: 0x60800007b140>{number = 5, name = (null)} 除了主线程,又开启了3个线程,并且任务是交替着同时执行的。所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始异步执行。

3. 串行队列 + 同步执行

不会开启新线程,在当前线程执行任务。任务是串行的,执行完一个任务,再执行下一个任务 NSLog(@"syncSerial---begin"); dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1------%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2------%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"3------%@",[NSThread currentThread]); } }); NSLog(@"syncSerial---end");

输出

syncSerial---begin 1------<NSThread: 0x608000064a00>{number = 1, name = main} 1------<NSThread: 0x608000064a00>{number = 1, name = main} 2------<NSThread: 0x608000064a00>{number = 1, name = main} 2------<NSThread: 0x608000064a00>{number = 1, name = main} 3------<NSThread: 0x608000064a00>{number = 1, name = main} 3------<NSThread: 0x608000064a00>{number = 1, name = main} syncSerial---end 所有任务都是在主线程中执行的,并没有开启新的线程。而且由于串行队列,所以按顺序一个一个执行。所有任务都在打印的syncConcurrent—begin和syncConcurrent—end之间,这说明任务是添加到队列中马上执行的

4. 串行队列 + 异步执行 - 会开启新线程,但是因为任务是串行的,执行完一个任务,再执行下一个任务

NSLog(@"asyncSerial---begin"); dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1------%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2------%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"3------%@",[NSThread currentThread]); } }); NSLog(@"asyncSerial---end");

输出

asyncSerial---begin asyncSerial---end 1------<NSThread: 0x608000267800>{number = 3, name = (null)} 1------<NSThread: 0x608000267800>{number = 3, name = (null)} 2------<NSThread: 0x608000267800>{number = 3, name = (null)} 2------<NSThread: 0x608000267800>{number = 3, name = (null)} 3------<NSThread: 0x608000267800>{number = 3, name = (null)} 3------<NSThread: 0x608000267800>{number = 3, name = (null)} 开启了一条新线程,但是任务还是串行,所以任务是一个一个执行。所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。

But别忘记了我们的主队列 主队列:GCD自带的一种特殊的串行队列

所有放在主队列中的任务,都会放到主线程中执行可使用dispatch_get_main_queue()获得主队列 So 我们还有两种了

1.主队列 + 同步执行 互等卡住不可行(在主线程中调用)

NSLog(@"syncMain---begin"); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1------%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2------%@",[NSThread currentThread]); } }); dispatch_sync(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"3------%@",[NSThread currentThread]); } }); NSLog(@"syncMain---end");

输出

2017-06-07 17:04:12.154 GCD[20860:682212] syncMain---begin (lldb)

原因:我们把任务放在了主队列中,也就放在了主线程的队列中。同步执行就是对任务立马执行。把第一个任务放在了主队列中,立马执行,但是主队列还要继续往下走,主队列中第一个任务要执行,主队列却要继续执行第二个任务第三个,so就卡住了。

假设不在主线程中调用 不会开启新线程,执行完一个任务,再执行下一个任务(在其他线程中调用)

dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ [self syncMain]; }); syncMain---begin 1------<NSThread: 0x60000007ed80>{number = 1, name = main} 1------<NSThread: 0x60000007ed80>{number = 1, name = main} 2------<NSThread: 0x60000007ed80>{number = 1, name = main} 2------<NSThread: 0x60000007ed80>{number = 1, name = main} 3------<NSThread: 0x60000007ed80>{number = 1, name = main} 3------<NSThread: 0x60000007ed80>{number = 1, name = main} syncMain---end 在其他线程中使用主队列 + 同步执行可看到:所有任务都是在主线程中执行的,并没有开启新的线程。而且由于主队列是串行队列,所以按顺序一个一个执行。同时我们还可以看到,所有任务都在打印的syncConcurrent—begin和syncConcurrent—end之间,这说明任务是添加到队列中马上执行的。

2. 主队列 + 异步执行 只在主线程中执行任务,执行完一个任务,再执行下一个任务

NSLog(@"asyncMain---begin"); dispatch_queue_t queue = dispatch_get_main_queue(); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"1------%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"2------%@",[NSThread currentThread]); } }); dispatch_async(queue, ^{ for (int i = 0; i < 2; ++i) { NSLog(@"3------%@",[NSThread currentThread]); } }); NSLog(@"asyncMain---end");

输出

asyncMain---begin asyncMain---end 1------<NSThread: 0x600000073dc0>{number = 1, name = main} 1------<NSThread: 0x600000073dc0>{number = 1, name = main} 2------<NSThread: 0x600000073dc0>{number = 1, name = main} 2------<NSThread: 0x600000073dc0>{number = 1, name = main} 3------<NSThread: 0x600000073dc0>{number = 1, name = main} 3------<NSThread: 0x600000073dc0>{number = 1, name = main} 我们发现所有任务都在主线程中,虽然是异步执行,具备开启线程的能力,但因为是主队列,所以所有任务都在主线程中,并且一个接一个执行所有任务是在打印的syncConcurrent—begin和syncConcurrent—end之后才开始执行的。说明任务不是马上执行,而是将所有任务添加到队列之后才开始同步执行。 开发中大多数还是用的GCD,那就多说点

补充1: GCD的栅栏方法

假设你想异步执行两组操作,一组操作完才可以执行另一组,dispatch_barrier_async就应运而生了

dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT); dispatch_async(queue, ^{ NSLog(@"----1-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----2-----%@", [NSThread currentThread]); }); dispatch_barrier_async(queue, ^{ NSLog(@"----barrier-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----3-----%@", [NSThread currentThread]); }); dispatch_async(queue, ^{ NSLog(@"----4-----%@", [NSThread currentThread]); });

输出

----1-----<NSThread: 0x60000007e080>{number = 3, name = (null)} ----2-----<NSThread: 0x608000077e80>{number = 4, name = (null)} ----barrier-----<NSThread: 0x608000077e80>{number = 4, name = ----4-----<NSThread: 0x60000007e080>{number = 3, name = (null)} ----3-----<NSThread: 0x608000077e80>{number = 4, name = (null)}

上面执行完,才执行下面的

补充2:GCD的延时执行方法

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ // 2秒后异步执行这里的代码... NSLog(@"run-----"); });

补充3:GCD只执行一次

使用dispatch_once函数能保证某段代码在程序运行过程中只被执行1次。

补充4:GCD快速迭代方法

和for循环遍历一样,GCD中是dispatch_apply,而且是同时遍历

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); dispatch_apply(6, queue, ^(size_t index) { NSLog(@"%zd------%@",index, [NSThread currentThread]); });

输出

0------<NSThread: 0x600000077d40>{number = 1, name = main} 2------<NSThread: 0x6000002636c0>{number = 3, name = (null)} 3------<NSThread: 0x608000262380>{number = 5, name = (null)} 1------<NSThread: 0x608000262240>{number = 4, name = (null)} 4------<NSThread: 0x600000077d40>{number = 1, name = main} 5------<NSThread: 0x6000002636c0>{number = 3, name = (null)}

补充5: GCD的队列组

有多个耗时操作,全完成之后,再返回主线程,就要用到队列组 调用队列组的dispatch_group_notify回到主线程执行操作。

dispatch_group_t group = dispatch_group_create(); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行1个耗时的异步操作 }); dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 执行1个耗时的异步操作 }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 等前面的异步操作都执行完毕后,回到主线程... });
转载请注明原文地址: https://www.6miu.com/read-53536.html

最新回复(0)