多线程概念
- 程序: 由源代码生成的可执行应用.
- 进程: 一个正在运行的程序可以看做一个进程. (例如: 正在运行的QQ就是一个进程) , 进程拥有独立运行所需的全部资源.
- 线程: 程序中独立运行的代码段.
注:一个进程是由一或多个线程组成. 进程只负责资源的调度和分配,线程才是程序真正的执行单元,负责代码的执行.
- 单线程:
- 每个正在运行的程序(即 进程),至少包含一个线程, 这个线程叫 主线程
- 主线程在程序启动时被创建,用于执行mian函数
- 只有一个主线程的程序,称作单线程程序
- 在单线程程序中,主线程负责执行程序的所有代码(UI 展现以及刷新,网络请求, 本地存储等). 这些代码只能顺序执行, 无法并发执行 .
- 多线程:
- 拥有多个线程的程序,称作多线程程序
- iOS 允许用户自己开辟新的线程, 相对于主线程来讲, 这些线程, 称作子线程
- 可以根据需要开辟若干子线程
- 子线程和主线程 都是 独立 的运行单元, 各自的执行互不影响, 因此能够并发执行
- 单线程,多线程区别 :
- 单线程程序: 只有一个线程, 即主线程, 代码顺序执行,容易出现代码阻塞(页面假死)
- 多线程程序: 有多个线程, 线程间独立运行, 能有效的避免代码阻塞,并且提高程序的运行性能.
注意: iOS关于UI的添加和刷新必须在主线程中操作.
- iOS多线程实现种类
- NSObject
- NSThread
- NSOperationQueue
- GCD
NSObject 和 NSThread
#pragma mark ---------NSObject 开辟子线程
//
//
//
#pragma mark ---------NSThread 手动开辟子线程
// NSThread
开辟一个子线程
//
参数
1: target
//
参数
2: action
//
参数
3:
传参
NSThread
*thread = [[
NSThread
alloc
]
initWithTarget
:
self
selector
:
@selector
(sayHi)
object
:
nil
];
//
开启子线程
[thread
start
];
//
//
//
//
#pragma mark -----------NSThread 自动开辟一个线程
}
- (
void
)sayHi{
// [NSThread currentThread];
获取当前的线程
NSLog
(
@" %@ "
, [
NSThread
currentThread
]);
// [NSThread mainThread];
获取主线程
NSLog
(
@" %@ "
, [
NSThread
mainThread
]);
// [NSThread isMainThread]
判断当前线程是不是主线程
NSLog
(
@" %d "
, [
NSThread
isMainThread
]);
#pragma mark ----------NSObject
//NSObject
中回到主线程去做某事
//
参数
1:
回到主线程做的事情
//
参数
2:
传递的参数
//
参数
3:
知道当前的线程已经结束才去做
[
self
performSelectorOnMainThr ead
:
@selector
(onMainThread)
withObject
:
nil
waitUntilDone
:
YES
];
#pragma mark -----------NSThread 手动
for
(
int
i = 0; i < 10000; i++) {
NSLog
(
@" %d "
, i);
if
(i == 5000) {
//
关闭线程
//
写在哪里
哪个线程就关闭了
,
注意
不要随意的使用
.
使用的时候一定要注意当前的线程
是否主线程
[
NSThread
exit
];
}
#pragma mark -----------NSThread 手动
}
- (
void
)onMainThread{
}
NSOperation和NSOperationQueue
- NSOperation :
- NSOperation类, 在MVC中属于M, 是用来封装单个任务相关的代码和数据的抽象类
- 因为它抽象的,不能够直接使用这个类,而是使用子类(NSInvocationOperation或NSBlockOperation) 来执行实际任务.
- NSOperation(含子类),
只是一个操作, 本身无主线, 子线程之分, 可在任意线程中使用. 通常与NSOperationQueue结合使用.
- NSOperationQueue
- NSOperationQueue是操作队列,它用来管理一组Operation对象的执行,会根据需要自动为Operation开辟合适数量的线程,以完成任务并行执行
- 其中NSOperation可以调节它在队列中的优先级 (使用addDependency: 设置依赖关系)
- 当最大并发数设置为1的时候,能实现线程同步 (串行执行);
- (
void
)viewDidLoad {
#pragma mark —————NSInvocationOperation
#pragma mark —————NSBlockOperation
}
- ( void )hehehe{
}
- (
void
)touchesBegan:(
NSSet
<</span>UITouch *> *)touches withEvent:(UIEvent *)event{
//NSOperationQueue 是一个队列管理器,可以根据operation任务自己,分配线程,自己管理线程的生命周期
//在开发过程中,我们不需要管理线程的创建和销毁
//NSOperationQueue 创建的是n个并行的线程
NSOperationQueue *queue = [[NSOperationQueuealloc]init];
//最大线程并发数
//设置这个参数之后,NSOperationQueue表示同时执行任务的最大数量
//即使只执行一个任务,系统有时候也会开辟多个线程去执行这个任务
queue.maxConcurrentOperationCo unt = 1;
for (int i = 0; i < 10; i++) {
NSBlockOperation *blockOperation = [NSBlockOperationblockOperationWithBlock:^{
NSLog(@"current --> %@ main --> %@",[NSThreadcurrentThread],[NSThreadmainThread]);
}];
[queue addOperation:blockOperation];
}
}
}
@end
GCD
Grand Central Dispatch (GCD)是Apple开发的一种多核编程技术.主要用于优化应用程序以支持多核处理器以及其他对称处理系统.
GCD提供函数实现多线程开发,性能更好,功能也更加强大.
核心概念:
- 任务: 具有一个订功能的代码段.一般是一个block或者函数
- 分发队列: GCD以队列的方式进行工作,FIFO(先入先出队列)
- GCD会根据分发队列的类型, 创建合适数量的线程执行队列中的任务
GCD中的两种队列 dispatch_queue:
- SerialQueue(串行) : 一次只执行一个任务. Serial queue通常用于同步访问,特定的资源或数据.当你创建多个Serial queue时,虽然他们各自是同步执行的,但Serial queue于Serial queue之间是并发执行的.SerialQueue能实现线程同步.
- Concurrent(并发) : 可以并发地执行多个任务,但是遵守FIFO(先入先出队列 )
- (
void
)viewDidLoad {
[
super
viewDidLoad
];
#pragma mark ----------GCD 串行队列
// 1)
系统提供的一个串行队列
#pragma mark ----------GCD 串行队列
//
//
使用系统提供的串行队列
(
主队列,也就是在主线程里一次执行任务
)
//
//
//
参数
2:
系统提供好的一个宏
(DISPATCH_QUEUE_SERIAL=NULL)
// 这种方式创建的队列,会开辟子线程去执行任务
#pragma mark ----------GCD
并行队列
// 1)
使用系统提供的并行队列
//
//
参数
1:
表示队列的优先级
(
//
参数
2:
系统保留字段
dispatch_queue_t
queue = dispatch_get_global_queue(
DISPATCH_QUEUE_PRIORITY_BACKGROUND
, 0);
// 2)
创建并行队列
//
#pragma mark ----------GCD
功能
-
//使用dispatch_async() 向队列添加任务,任务会排队执行
-
// dispatch_after();
-
//dispatch_apply();
-
//分组
}
- dispatch_once()
//将任务添加到队列, 但任务在程序运行过程中,只执行一次
#import
"MyObject.h"
static MyObject *object = nil ;
@implementation MyObject
+ ( MyObject *)sharedMyObject{
//
表示同意时间内
,
只有一个线程可以访问
block
块里面的内容
//dispatch_once
系统封装好的代码块
static
dispatch_once_t
onceToken;
dispatch_once
(&onceToken, ^{
if
(
object
==
nil
) {
object
= [
MyObject
new
];
}
});
return
object
;
}
static MyObject *object = nil ;
@implementation MyObject
+ ( MyObject *)sharedMyObject{
}
@end
- dispatch_sync()
//将任务添加到队列, block不执行完,下面代码不会执行 - dispatch_async()
//将任务添加到队列,不等内部block执行完,就去执行下面代码 - dispatch_async_f()
//将任务添加到队列, 任务是函数,非Block
线程间通信
线程间通信分为两种:
- 主线程进入子线程 (前面的方法都可以)
- 子线程回到主线程
#pragma mark ----------NSObject
}
- (
void
)onMainThread{
}
#pragma mark —————GCD
- (
void
)loadData{
NSURLSession
*session = [
NSURLSession
sharedSession
];
NSURLSessionTask
*task = [session
dataTaskWithURL
:[
NSURL
URLWithString
:
@"https://www.baidu.com"
]
completionHandler
:^(
NSData
*
_Nullable
data,
NSURLResponse
*
_Nullable
response,
NSError
*
_Nullable
error) {
if
(error ==
nil
) {
dispatch_async
(
dispatch_get_main_queue
(), ^{
//
这里去做更新
UI
的事情
});
}
}];
}
- 线程互斥
- 多线程并行编程中,线程间同步与互斥是一个很有技巧也是很容易出错的地方.
- 线程间互斥应对的是这种场景: 多个线程操作统一资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确.