1.使用单利模式时,可以使用GCD
2.耗时操作放入子线程处理,完成后回主线程显示。
3.从数据库读取大量数据,可开辟子线程操作。
4.处理音频、视频数据时,在子线程处理。
5.数据同步操作。
NSThread(OC语言,可以手动开辟的子线程,需要自己管理内存): 使用更加面向对象,相对简单,可以直接操作线程对象,偶尔使用。
GCD(C语言,自动管理生命周期): 是NSOperation的底层实现,是c语音级别的多线程,用block(代码块)来表示一个任务 GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程);
NSOperation(GCD的封装,管理生命周期): 基于GCD,比GCD多了一些更简单实用的功能,更加面向对象,经常使用。
###NThread的线程创建的三种方式
1.NSObject的
performSelectorInBackground / performSelectorOnMainThread
2.NSThread 的
detachNewThreadSelector,创建一个新线程并自动执行
3.NSthread 的
alloc initWithtarget,创建一个新线程但不会自动执行的
###NSThread常用方法
当前线程休眠几秒 [NSThread sleepForTimeInterval:1.0f];
休眠到指定时间 [NSThread sleepUntilDate:date];
YES表示暂停,NO表示恢复 [queue1 setSuspended:YES];
[self performSelector:@selector(function) onThread:[NSThread mainThread]
withObject:nil waitUntilDone:YES];
waitUntilDone 是否马上执行
YES:表示马上执行
NO:表示加入onThread所在队列中。等待runloop调度执行
注意:如果onThread还是当前线程,就相当于直接调用一个方法
是NSOperation的底层实现,是c语音级别的多线程,用block(代码块)来表示一个任务 GCD 会自动管理线程的生命周期(创建线程、调度任务、销毁线程);
GCD的队列queue 分为三种:全局并行queue;主线程串行queue; 自定义queue
1.获取全局队列 dispatch_get_global_queue(queue优先级,0)
2.获取主线程队列dispatch_get_main_queue()
3.创建自定义队列dispatch_queue_create(“唯一标识”,队列类型)
队列类型说明:
DISPATCH_QUEUE_SERIAL表示串行队列 ; DISPATCH_QUEUE_CONCURRENT表示并行队列
全局队列又分为: Hight;default ;low; background四种全局队列。
其中 主队列是在主线程中顺序执行,全局队列是在子线程种并发执行的,所以全局队列也叫子线程队列。
GCD各种队列背后的线程模型是一个苹果自己维护的线程池。
最底层是主线程和gcd线程池,其中主线程存放主线程队列,gcd线程池存放各种级别的全局队列 在主线程创建的串行队列会自动存入主线程队列
在子线程中创建的全局队列就会存到相应优先级的全局队列里,并且依次优先级成树形排列
GCD的线程开启后无法停止,但是可以设置条件停止线程中的任务,让线程空闲,减少cpu的消耗
异步提交代码块到某个队列
dispatch_async(queue, ^{
});
同步提交代码块到某个队列
dispatch_sync(queue1, ^{
});
异步提交代码块到串行队列,线程池将在指定延迟时间5s后执行任务
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5*NSEC_PER_SEC)), queue2, ^{
});
多个任务完成后,需要一个统一的回调通知去处理接下来的业务,这个时候就需要使用dispatch_group_notify及dispatch_gruop_enter() dispatch_group_lever来处理
// 1.创建Dispatch_group
dispatch_group_t group = dispatch_group_create();
for (int i = 0; i < 3; i ++){
//2.1 声明dispatch_group_enter(group)下面的任务由group组管理,group组的任务数+1
dispatch_group_enter(group);
//2.将block任务添加到queue队列,并被group组管理 dispatch_group_async
dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
sleep(3);
NSLog(@"异步任务开启!,当前线程:%@",[NSThread currentThread]);
});
//2.2 dispatch_group_leave相应的任务执行完成,group组的任务数-1
dispatch_group_leave(group);
}
//3.所有Dispatch_group后的汇总操作
//dispatch_group_notify只会等待任务的完成,不会阻塞线程
dispatch_group_notify(group, dispatch_get_main_queue(), ^(){
// 主线程处理
NSLog(@"主线程执行,当前线程:%@",[NSThread currentThread]);
});
});
分别表示某个任务进入到group中,受到group管理,在group内部会有一个计数器,调用dispatch_group_enter后,该计数器会+1,调用dispatch_group_leave后该计数器会-1,当计数器为0时,则主动调用dispatch_group_notify的block,否则一直处于等待中,直到所有enter的任务都调用完leave,因此,一个任务真正的执行完的标志是调用了dispatch_group_leave,否则dispatch_group_notify会一直处于等待中,因此,dispatch_group_enter和dispatch_group_leave一定要配套使用。
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
wait会阻塞主线程,等待 dispatch_group中的任务执行,当执行完毕后,或者超过了dispatch_time_t设置的时间,就会结束这个方法,执行剩下的任务。
(这里注意dispatch_group_notify只会等待任务的完成,不会阻塞线程)
NSOperation与NSOperationQueue来实现多线程,是基于GCD更高一层的封装,是完全面向对象。比GCD更简单易用、代码可读性也更高。
是对GCD的封装,可以理解为gcd的oc实现,于GCD相比好处是提供了任务的暂停, 取消. 恢复依赖等方法
其中NSOPeration代表一个多线程任务,是个抽象类不可直接使用,使用的是它的子类NSInvacationOperation和NSBlockOperation
NSOperationQueue:负责管理添加到队列中的任务,加入的任务会自动在子线程中异步执行,执行顺序会根据线程间的依赖关系和优先级来决定
gcd创建的线程,系统自动管理其生命周期,程序无法控制,但是可以通过添加条件来停止线程中的任务,让线程无任务可做,从而减少cpu的消耗
1.创建操作:先将需要执行的操作封装到一个 NSOperation 对象中。 2.创建队列:创建 NSOperationQueue 对象。 3。将操作加入到队列中:即:将 NSOperation 对象添加到 NSOperationQueue 对象中。之后呢,系统就会自动将 NSOperationQueue 中的 NSOperation 取出来,在新线程中执行操作。
1.NSInvocationOperation 通过selector快速的构建一个operation
2.NSBlockOperation通过addExecutionBlock来添加多个执行block来实现并发执行一个或则多个block,只有所有block执行完毕,operation才算执行完毕,所以一个NSBlockOperation可以有多个线程。
如果一个 NSBlockOperation 添加多个执行block。NSBlockOperation 是否开启新线程,取决于操作的个数。如果添加的操作的个数多,就会自动开启新线程。当然开启的线程数是由系统来决定的。
3.自定义NSOperation子类的做法是继承NSOperation;重写main方法;给用户提供接口,在main中自己实现线程的事件处理
1.添加操作间依赖关系
[op2 addDependency:op1]; // 让op2 依赖于 op1,则先执行op1,在执行op2
2.设置操作的优先级
[o3 setQueuePriority:NSOperationQueuePriorityHigh];
3.NSBlockOperation可通过completionBlock来添加所有任务完成后的下一步操作,可用于任务间的依赖执行。
1,先看operation是否准备好,准备好的看它们之间的依赖关系
2.在看operation的优先级,默认都是普通级别,从优先级高的开始执行
NSOperationQueue:队列,负责管理添加到队列中的多线程任务,底层维护了一个线程池。加入的任务会自动在子线程中异步执行,执行顺序会根据线程间的依赖关系和优先级来决定, NSOperationQueue一共有两种队列:主队列、自定义队列。其中自定义队列同时包含了串行、并发两种队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
加入队列中任务会自动异步执行.加入主队列的任务在主线程串行执行,加入其他线程的任务是并发执行的
queue.maxConcurrentOperationCount = 1; // 串行队列
maxConcurrentOperationCount 控制的不是并发线程的数量,而是一个队列中同时能并发执行的最大操作数。而且一个操作也并非只能在一个线程中运行。
1.单个任务NSOperation启动都是在当前线程同步执行
2.使用NSOperationQueue管理任务operation,加入队列的任务会自动异步并发执行。
3.添加到quete中的operation对象随时可能执行,因此加入quete后不可修改operation的状态。
4.NSBlockOperation的多个任务是并发执行的,有的在当前线程,有的在其他线程,但NSBlockOperation的completionBlock,会在blockoperation的所有任务完成后执行。并且是在当前线程执行
5.设置队列最大并发数(就是队列一次并发执行operation的个数),不是线程个数,所以1也不能确保是串行,因为blockoperation可以有多个block,每个block都是一个线程,但是只有一个operation。
1.operation是否准备好,由operation间的依赖关系决定
2.operation的优先级,默认都是普通级别。
CDG:Grand Central Dispatch 是NSoperation的底层实现,是c语言级别的多线程,用block代码块表示一个任务,会自动利用cpu的多核特性并行运算,会自动管理线程的生命周期(创建线程、调度任务、销毁线程)也就是说程序员只需要告诉GCD想要执行什么任务,不需要写任何线程管理代码.
NSOperation,基于GCD,是面对对象的,但是GCD更简单易用、代码可读性也更高。 可以添加任务依赖,来保证执行顺序的正确性,而且已开启的任务可以取消,暂停,恢复,也可以设置优先级
GCD高效,NSOperation开销相对高
线程安全就是线程同步,就是不允许在同一时刻在不同的线程中操作同一变量,说白了就是一个对象只有一个线程使用完成才能让其他线程访问,所有多线程对数据的访问需要加锁
1.@synchronized (主要应用创建单利)
2.NSLock线程锁,简单的互斥锁,需要lock与unlock对应,不能嵌套使用
3.NSConditionLock 条件锁,手动控制线程wait和signal
4.NSRecursiveLock 递归锁
5.GCD的并发控制-信号量semaphore
死锁通常指两个线程a和b都卡住了,a在等待b完成才能继续,b在等待a完成才能继续执行,这样大家都完成不了,所以导致死锁
死锁出现的条件是串行队列的同步执行,因为同步会阻塞当前线程,但是串行要等待当前线程完成才能执行,所以就产生了死锁
先看依赖关系,然后从准备好的优先级最高的那个执行
主线程的异步执行 在主线程队列中的异步执行,相当于子线程的作用,但不会产生子线程,会在主线程无任务时执行。
用于数据准备好后的一种依赖执行,而且可以保证数据准备完成后执行,好处是不会产生子线程,不会阻塞主线程
原因:系统资源不足,进程的推进顺序不合适。资源分配不足,比如互斥条件,请求与条件保持,不可剥夺条件,循环等待条件,线程死锁,
互斥条件:及一个资源一次只能被一个进程使用 请求与保持条件:一个进程因请求资源而阻塞时对已获得的资源保持不放(解决办法资源一次性分配) 不可剥夺条件:进程已获得资源。在未使用完之前。不可强行剥夺(解决办法可剥夺资源) 循环等待条件;若干进程之间形成一种头尾相接的循环等待资源的关系(解决办法资源有序分配)
1)鸵鸟政策,及视而不见 2)银行家算法,分配之前看清楚,如果会导致死锁则不分配,不会导致死锁才分配
/** 栅栏(用于需要依次执行完多个线程组) */
//并发队列异步执行代码块1,2
dispatch_async(queue, ^{
//代码块1
});
dispatch_async(queue, ^{
//代码块2
});
//1,2执行完后才会执行3,4
dispatch_barrier_async(queue, ^{
});
定义:信号量就是一种可用来控制访问资源数量的标识,设置一个信号量,在线程访问之前,加上信号量的处理,则可控制系统按照指定的信号量数量来执行多个线程 信号量相关函数: 1.创建信号量dispatch_semaphore_create(信号量数) 2.降低信号量 dispatch_semaphore_wait//信号量减一 3.提高信号量dispatch_semaphore_signal//信号量加一 正常的使用逻辑是:先降低再提高,而且这两个函数一般成对使用 如果dispatch_semaphore_wait减1前如果小于1,则一直等待。同时这两者之间是线程无顺序的抢占资源,但只能允许一个线程执行。所以猜测如果某一时刻进行了dispatch_semaphore_signal操作,则会继续执行
//1.创建GCD信号量dispatch_semaphore
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
//2.创建队列dispatch_queue
dispatch_queue_t quene = dispatch_get_global_queue(0, 0);
//3.创建gcd任务1并添加到队列中
dispatch_async(quene, ^{
//3.1信号量减一dispatch_semaphore_wait
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 1");
sleep(1);
NSLog(@"complete task 1");
//3.2gcd信号量加1spatch_semaphore_signal
dispatch_semaphore_signal(semaphore);
});
///3.创建gcd任务2并添加到队列中
dispatch_async(quene, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
NSLog(@"run task 2");
sleep(1);
NSLog(@"complete task 2");
dispatch_semaphore_signal(semaphore);
});
NSCondition *condition = [[NSCondition alloc] init];
NSMutableArray *products = [NSMutableArray arrayWithCapacity:0];
//消费者
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
//加锁
[condition lock];
if([products count]==0)
{
NSLog(@"wait for Product");
//wait 让当前线程处于等待状态
[condition wait];
}
//消费一个产品
[products removeObjectAtIndex:0];
NSLog(@"custome a product");
//解锁
[condition unlock];
}
});
//生产者
dispatch_async(dispatch_get_global_queue(0, 0), ^{
while (1) {
//加锁
[condition lock];
//制作一个产品
NSLog(@"make a product");
[products addObject:[[NSObject alloc] init]];
//所有处于wait的线程,均有机会开始执行
[condition signal];
//解锁
[condition unlock];
sleep(1);
}
});
// GCD定时器
static dispatch_source_t _timer;
- (void)startGCDTimer {
if (_timer) return;
//1设置时间间隔
NSTimeInterval period = 0.05;
//2.创建存放定时器的queue
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//3.创建定时器timer
_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
//4.设置timer
dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0);
// 5.添加timer的事件回调
dispatch_source_set_event_handler(_timer, ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self upDateProgress];
});
});
// 6.开启定时器
dispatch_resume(_timer);
//6.1关闭定时器
dispatch_source_cancel(_timer);
}
dispatch_source_t 是间隔定时器,也就是说每隔一段时间间隔定时器就会触发
dispatch_source_set_timer 中第二个参数,当我们使用dispatch_time 或者 DISPATCH_TIME_NOW 时,系统会使用默认时钟来进行计时。然而当系统休眠的时候,默认时钟是不走的,也就会导致计时器停止。使用 dispatch_walltime 可以让计时器按照真实时间间隔进行计时。
dispatch_source_set_event_handler 这个函数在执行完之后,block 会立马执行一遍
###NSthread 小知识
休眠5s
// [NSThread sleepForTimeInterval:5];
中止当前线程
// [NSThread exit];
qualityOfService 属性来设置线程的优先级
NSLog(@"子线程运行:%@ %@ 优先级:%d", [NSThread currentThread], object, [NSThread currentThread].qualityOfService);
####GCD-dispatch_apply函数说明
该函数按指定的次数将指定的Block追加到指定的Dispatch Queue中,并等到全部的处理执行结束
10 指定重复次数 指定10次 //queue 添加block的queur // index 带有参数的Block, index的作用是为了按执行的顺序区分各个Block
dispatch_apply(10, queue, ^(size_t index) {
NSLog(@"%zu", index);
});
NSLog(@"done");
1.注意线程间的通信,数据共享,数据安全等问题 2.多线程会将耗时操作放在后台执行,避免了阻塞主线程,而且在iOS中UI绘制和用户响应都必须在主线程。
DISPATCH_QUEUE_PRIORITY_HIGH: //高
DISPATCH_QUEUE_PRIORITY_DEFAULT://默认
DISPATCH_QUEUE_PRIORITY_LOW: //低
DISPATCH_QUEUE_PRIORITY_BACKGROUND: //后台
// 1.创建 NSInvocationOperation 对象
NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
// 2.调用 start 方法开始执行操作
[op start];
// 1.创建 NSBlockOperation 对象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
NSLog(@"1---%@", [NSThread currentThread]); // 打印当前线程
}
}];
// 2.添加额外的操作
[op addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
NSLog(@"2---");
}
}];
// 3.调用 start 方法开始执行操作
[op start];
}
3.1 继承NSopeation定义一个新类
3.2新类中改写main方法来实现自己的线程事件处理
- (void)main {
if (!self.isCancelled) {
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2];
NSLog(@"1---%@", [NSThread currentThread]);
}
}
}
@end