进程Process:系统中
正在运行的一个
应用程序
线程Thread:是程序的 一段执行序列,是进程的 一部分
线程Thread:是程序的 一段执行序列,是进程的 一部分
任务Task: 线程执行的一段序列(逻辑)
进程线程的关系
1. 进程不执行任务,只有线程才执行任务(逻辑)
2. 活动监视器:线程最小值:1个 —> 进程至少有一个线程
1. 进程不执行任务,只有线程才执行任务(逻辑)
2. 活动监视器:线程最小值:1个 —> 进程至少有一个线程
3.
主线程
:进程至少有一个线程,该线程叫主线程
多线程
四种创建子线程的方式
1. Pthread (了解; 几乎不使用):
POSIX Thread (Portable Operation System Interface): 可移植的操作系统的接口
1) 基于C语言的接口
1) 基于C语言的接口
2) 多平台
1.
创建一个子线程对象
/** 第二个参数 : 指定线程的一些属性 ( 栈空间 )(NULL: 使用默认的属性 )
第三个参数 : 指向函数的指针 ( 传参类型 :void *; 返回值类型 :void *)
第四个参数 : 传给 task 的参数
*/
char *data = "hello" ;
pthread_t pthread;
int threadError = pthread_create (&pthread, NULL , task , data);
if (threadError != 0 ) {
NSLog ( @" 无法创建子线程 " );
/** 第二个参数 : 指定线程的一些属性 ( 栈空间 )(NULL: 使用默认的属性 )
第三个参数 : 指向函数的指针 ( 传参类型 :void *; 返回值类型 :void *)
第四个参数 : 传给 task 的参数
*/
char *data = "hello" ;
pthread_t pthread;
int threadError = pthread_create (&pthread, NULL , task , data);
if (threadError != 0 ) {
NSLog ( @" 无法创建子线程 " );
}
//2.
将耗时的操作给子线程做
//<#void *(*)(void *)#>
//void * 类型 : 函数可以返回任意类型的指针 ( 无类型 )
//void * 类型 : 函数可以返回任意类型的指针 ( 无类型 )
//void:
函数没有返回值
void
* task(
void
*data) {
printf ( "Number: %s" , ( char *)data);
// 执行耗时操作 ( 子线程 )
for ( int i = 0 ; i < 20 ; i++) {
NSLog ( @" 执行次数 :%d" , i);
}
return 0 ;
printf ( "Number: %s" , ( char *)data);
// 执行耗时操作 ( 子线程 )
for ( int i = 0 ; i < 20 ; i++) {
NSLog ( @" 执行次数 :%d" , i);
}
return 0 ;
}
2. NSThread (理解: 其中的一些术语):
优势:基于OC接口;使用方便
缺点:手动管理线程的生命周期 (提高编程的门槛)
三种方式创建子线程
第一种方式:init方式 (多) —> 创建和启动是分离
第二种方式:detach方式 (多) —> 自动创建/启动
第二种方式:detach方式 (多) —> 自动创建/启动
第三种方式:perform方式 (了解:用的较少; iOS9不推荐)
1.
适应场景
:
创建线程和启动线程的逻辑分开
(
灵活性高
)
//1.
创建
NSThread
对象
(selector
指定子线程要执行的方法
; object:
传给
selector
方法的参数
)
NSThread
*thread = [[
NSThread
alloc
]
initWithTarget
:
self
selector
:
@selector
(downloadTask:)
object
:
@"
传入参数
"
];
// 设置 thread 对象的名字
thread. name = @" 子线程 " ;
//2. 指定线程对象执行耗时操作
//3. 手动启动该线程对象
// 设置 thread 对象的名字
thread. name = @" 子线程 " ;
//2. 指定线程对象执行耗时操作
//3. 手动启动该线程对象
[thread start];
2.
适用场景
:
简单地在子线程中执行
selector
方法
(
无法指定线程的名字
)
自动的启动一个子线程
,
执行
selector
方法
[self performSelectorInBackground:@selector(downloadTask:) withObject:nil];
3.
适用场景
:
简单地在子线程中执行
selector
方法
(
创建和启动没有分离
)
自动的创建子线程
;
并且自动启动该子线程
[NSThread detachNewThreadSelector:@selector(downloadTask:) toTarget:self withObject:nil];
备注:
1. [NSThread currentThread] —> {number = 1, name = main}: 一定是在 主线程 中执行
2. [NSThread currentThread] —> {number = 2, name = null}: 一定是在 子线程 中执行 (只要是number非1,就是在子线程)
3. 明确代码执行的顺序
设定线程的优先级(0.0 ~ 1.0; 值越大,优先级越高)
+ ( double )threadPriority;
1. [NSThread currentThread] —> {number = 1, name = main}: 一定是在 主线程 中执行
2. [NSThread currentThread] —> {number = 2, name = null}: 一定是在 子线程 中执行 (只要是number非1,就是在子线程)
3. 明确代码执行的顺序
设定线程的优先级(0.0 ~ 1.0; 值越大,优先级越高)
+ ( double )threadPriority;
+ (BOOL)setThreadPriority:(double)p;
添加
互斥锁
(最常用的一种 ): 提供/
保证
了每次
只有一个
线程可以执行代码的有效保护
形式
。
锁: NSLock (推荐)
加锁的时机:开始卖票之前
锁: NSLock (推荐)
加锁的时机:开始卖票之前
解锁的时机:卖完一张之后/全部卖完
#import
"ViewController.h"
@interface
ViewController
()
//
剩余多少票
@property
(
nonatomic
,
assign
)
int
leftTicketCount;
//
已经卖出多少
@property
(
nonatomic
,
assign
)
int
soldTicketCount;
//
锁属性
@property
(
nonatomic
,
strong
)
NSLock
*lock;
@end
@implementation
ViewController
- ( void )viewDidLoad {
[ super viewDidLoad ];
// 设置属性的初始值
self . soldTicketCount = 0 ;
- ( void )viewDidLoad {
[ super viewDidLoad ];
// 设置属性的初始值
self . soldTicketCount = 0 ;
self.leftTicketCount = 50;
//NSThread
窗口一
NSThread *thread = [[ NSThread alloc ] initWithTarget : self selector : @selector (saleTicket) object : nil ];
NSThread *thread = [[ NSThread alloc ] initWithTarget : self selector : @selector (saleTicket) object : nil ];
thread.name = @"
窗口一
"
;
NSThread
*secondThread = [[
NSThread
alloc
]
initWithTarget
:
self
selector
:
@selector
(saleTicket)
object
:
nil
];
secondThread.name = @"
窗口二
"
;
//
对锁初始化
self.lock = [NSLock new];
//
手动启动
[thread start ];
[secondThread start ];
[thread start ];
[secondThread start ];
}
//
模拟买票逻辑
- (void)saleTicket {
//
一直循环
while ( 1 ) {
// 加锁
[ self . lock lock ];
if ( self . leftTicketCount > 0 ) {
// 还有票
self . leftTicketCount --;
self . soldTicketCount ++;
NSLog ( @" 线程 :%@ 买票成功;剩余票数: %d; 已卖出: %d" ,[ NSThread currentThread ], self . leftTicketCount , self . soldTicketCount );
NSLog ( @"===============" );
// 解锁
[ self . lock unlock ];
} else {
// 卖完了
NSLog ( @" 卖完了额 " );
// 解锁
[ self . lock unlock ];
// 跳出循环
return ;
}
while ( 1 ) {
// 加锁
[ self . lock lock ];
if ( self . leftTicketCount > 0 ) {
// 还有票
self . leftTicketCount --;
self . soldTicketCount ++;
NSLog ( @" 线程 :%@ 买票成功;剩余票数: %d; 已卖出: %d" ,[ NSThread currentThread ], self . leftTicketCount , self . soldTicketCount );
NSLog ( @"===============" );
// 解锁
[ self . lock unlock ];
} else {
// 卖完了
NSLog ( @" 卖完了额 " );
// 解锁
[ self . lock unlock ];
// 跳出循环
return ;
}
}
}
@end
同步锁 (
不推荐
: 消耗更多的资源)
while (1) {
@synchronized(self) {
//将saleTicket中while逻辑放到此处
}
while (1) {
@synchronized(self) {
//将saleTicket中while逻辑放到此处
}
}
nonatomic:
非原子 (苹果推荐)
atomic: 原子(消耗资源) —> 对这个属性的set方法加锁, 线程安全
@property (atomic, assign) int leftTicketCount;
3.GCD
GCD (Grand Central Dispatch): 伟大的(NB)中枢调度(多线程)解决方案(Solution)
特点:
基于C语言的底层的接口
不知道任何和线程相关的概念(锁/线程对象) —> 降低开发门槛 (money)
所有子线程执行的任务都放到block中 (自动)
自动利用CPU多核的优势, 执行效率高
基于C语言的底层的接口
不知道任何和线程相关的概念(锁/线程对象) —> 降低开发门槛 (money)
所有子线程执行的任务都放到block中 (自动)
自动利用CPU多核的优势, 执行效率高
自动维护线程池, 自动调度各个线程对象
术语
任务: 执行什么操作(代码逻辑)
任务: 执行什么操作(代码逻辑)
队列: 用来存放需要执行的任务
通用步骤:
1. 创建一个空的 队列
2. 提交一个/多个任务到队列中
1. 创建一个空的 队列
2. 提交一个/多个任务到队列中
3. 执行队列中的任务
队列的类型:
一: 手动创建队列
一: 手动创建队列
串行队列 (Serial Queue): 一个一个顺序的执行
创建一个空的串行队列
dispatch_queue_t queue = dispatch_queue_create("FirstSerialQueue", DISPATCH_QUEUE_SERIAL);
并行队列 (Concurrent Queue): 多个任务
同时
执行
创建一个空的并行队列
dispatch_queue_t queue = dispatch_queue_create("FirstConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
二: 系统默认创建的队列
全局队列: 特殊的并行队列
全局队列
特殊的并行队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
主队列 (主线程执行): 特殊的串行队列
获取系统创建的主队列
dispatch_queue_t queue = dispatch_get_main_queue();
执行任务的方式:
同步执行 (Sync): 不会开启新的线程, 在
当前线程
中执行任务;
等待
一个任务, 再在执行另一个任务
把执行的任务放到队列中
dispatch_sync (queue, ^{
dispatch_sync (queue, ^{
//
要执行的任务的逻辑
});
异步执行 (Async): 会开启新的子线程执行任务; 立即返回(不会等待任务执行完毕)
把执行的任务放到队列中
dispatch_async (queue, ^{
dispatch_async (queue, ^{
//
要执行的任务的逻辑
});
队列任务的执行特点: FIFO(First In First Out)
总结常用组合:
并行队列+异步执行 (子线程执行block任务 + 立即返回)
全局队列+异步执行 (子线程执行block任务 + 立即返回)
并行队列+异步执行 (子线程执行block任务 + 立即返回)
全局队列+异步执行 (子线程执行block任务 + 立即返回)
主队列+异步执行 (主线程执行block任务 + 立即返回)
使用GCD的方式来下载图片(耗时操作: 子线程执行)
选择: 全局队列+异步执行
UIButton + UIImageView
图片的地址 (网址)URL(Uniform Resource Locator)
UIButton + UIImageView
图片的地址 (网址)URL(Uniform Resource Locator)
http:/xx/xxx/ss.png
总结:
1. 介绍下载图片的方式:
1. 介绍下载图片的方式:
NSString -> NSURL -> NSData -> UIImage
从子线程回到主线程的方法
1.使用NSObject方法: performSelectorOnMainThread
2.使用NSObject方法: performSelector:OnThread:[NSThread mainThread]
3.dispatch_async
(
dispatch_get_main_queue
(), ^{
//
回到主线程做的
});
4.[[
NSOperationQueue
mainQueue
]
addOperationWithBlock
:^{
//
回到主线程做的
}];
GCD把多个任务(下载图片)打包成一个组Group, 调用group_notify, 可以做其他的事情
全局队列
+
异步执行
dispatch_queue_t queue = dispatch_get_global_queue ( 0 , 0 );
// 创建 group 对象
dispatch_group_t group = dispatch_group_create ();
// 将任务提到 group 中
dispatch_group_async (group, queue, ^{
[ NSThread sleepForTimeInterval : 5 ];
NSLog ( @" 第一个图片下载完毕 %@" , [ NSThread currentThread ]);
dispatch_queue_t queue = dispatch_get_global_queue ( 0 , 0 );
// 创建 group 对象
dispatch_group_t group = dispatch_group_create ();
// 将任务提到 group 中
dispatch_group_async (group, queue, ^{
[ NSThread sleepForTimeInterval : 5 ];
NSLog ( @" 第一个图片下载完毕 %@" , [ NSThread currentThread ]);
});
通知
group
中的任务执行完了
(
合成图片
/
回到主线程
)
dispatch_group_notify (group, queue, ^{
// 三个图片已经全部下载完了 ( 子线程 )
NSLog ( @" 三个图片下载完毕 :%@" , [ NSThread currentThread ]);
// 回到主线程
dispatch_async ( dispatch_get_main_queue (), ^{
NSLog ( @" 回到主线程更新界面 " );
});
dispatch_group_notify (group, queue, ^{
// 三个图片已经全部下载完了 ( 子线程 )
NSLog ( @" 三个图片下载完毕 :%@" , [ NSThread currentThread ]);
// 回到主线程
dispatch_async ( dispatch_get_main_queue (), ^{
NSLog ( @" 回到主线程更新界面 " );
});
});
GCD一次性任务:
一次性任务在程序的整个生命周期只运行一次且一次(one and only once)
dispatch_once(—, ^block {})
4.NSOperation
队列类型
1.非主队列:NSOperationQueue alloc]init];
2.主队列:[NSOperationQueue mainQueue];
发送任务方式
operation start 同步任务
—> NSInvocationOperation的操作由主线程执行
—> NSBlockOperation类型的操作对象, 第一个是主线程执行, 剩余的由子线程执行
queue addOperation:operation 异步任务
—> NSInvocationOperation和NSBlockOperation都会启动新的子线程执行
常用类: NSOperation (抽象类)
NSInvocationOperation
创建非主队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
创建
NSInvocationOperation
对象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(printPlus) object:nil]
1.同步执行
[operation start];
直接添加到主队列中执行
;sync
同步执行
2.异步执行
[queue
addOperation
:operation];
指定执行的任务
(
添加的瞬间就异步执行
) 可以创建多个operation添加到queue
NSBlockOperation
方式一:
创建对象
NSBlockOperation
*operation = [
NSBlockOperation
new
];
// 添加任务
[operation addExecutionBlock :^{
NSLog ( @" 下载图片 111111%@" , [ NSThread currentThread ]);
}];
[operation addExecutionBlock :^{
NSLog ( @" 下载图片 2222%@" , [ NSThread currentThread ]);
}];
[operation addExecutionBlock :^{
NSLog ( @" 下载图片 3333%@" , [ NSThread currentThread ]);
}];
// 执行任务 ( 同步 )
// 第一个操作由主线程做 ; 剩余操作由子线程做
// 添加任务
[operation addExecutionBlock :^{
NSLog ( @" 下载图片 111111%@" , [ NSThread currentThread ]);
}];
[operation addExecutionBlock :^{
NSLog ( @" 下载图片 2222%@" , [ NSThread currentThread ]);
}];
[operation addExecutionBlock :^{
NSLog ( @" 下载图片 3333%@" , [ NSThread currentThread ]);
}];
// 执行任务 ( 同步 )
// 第一个操作由主线程做 ; 剩余操作由子线程做
[operation start];
方式二:
//1.创建一个非主队列
NSOperationQueue
*queue = [[
NSOperationQueue
alloc
]
init
];
//2. 创建操作对象 ( 方式二 )
NSBlockOperation *operation = [ NSBlockOperation blockOperationWithBlock :^{
NSLog ( @" 下载图片 1111%@" , [ NSThread currentThread ]);
}];
NSBlockOperation *otherOperation = [ NSBlockOperation blockOperationWithBlock :^{
NSLog ( @" 下载图片 2222%@" , [ NSThread currentThread ]);
}];
//3. 执行操作 ( 添加操作到非主队列 )
[queue addOperation :operation];
//2. 创建操作对象 ( 方式二 )
NSBlockOperation *operation = [ NSBlockOperation blockOperationWithBlock :^{
NSLog ( @" 下载图片 1111%@" , [ NSThread currentThread ]);
}];
NSBlockOperation *otherOperation = [ NSBlockOperation blockOperationWithBlock :^{
NSLog ( @" 下载图片 2222%@" , [ NSThread currentThread ]);
}];
//3. 执行操作 ( 添加操作到非主队列 )
[queue addOperation :operation];
[queue addOperation:otherOperation];
指定多少个操作同时执行; 设定任务中的依赖关系
设定同时执行任务的个数
(
起多少子线程
)
queue.maxConcurrentOperationCount = 2;
设定依赖关系
[thirdOperation
addDependency
:operation];//operation执行完以后thiread执行
[thirdOperation addDependency:secondOperation];//secondO
peration执行完以后thiread执行