多线程
GCD
1.任务和队列:
任务:要做什么,一段代码,在GCD中就是Block,任务有同步执行sync和异步执行async,二者区别是是否开辟新线程.
sync 阻塞当前进程并等待Block中任务执行完毕,然后线程才能继续执行下去
async 当前线程会直接往下执行,不会阻塞当前线程
队列:用于存放任务 串行队列和并行队列
串行队列:FIFO 一个一个先进后出执行
并行队列:并行队列中的任务也是FIFO取出来,不同的是,它取出来一个放到别的线程,然后再取出来一个又放到别的线程,由于取得动作快,看起来就是一起执行的,GCD会根据系统资源控制并行的数量,如果任务很多,不会让所有任务同时执行。
2.创建队列:
主队列:刷新UI,任何需要刷新UI的操作都在主队列进行,一般耗时的任务都放到别的线程执行
let queue = ispatch_get_main_queue()
自己创建的队列:第一个参数是标识符,用于DEBUG的时候标示唯一的队列,可以为空;第二个参数标示创建的队列是串行的还是并行的,nil或DISPATCH_QUEUE_SERAIL表示创建的是串行队列,参数DISPATCH_QUEUE_CONCURRENT表示创建的是并行队列
//串行队列
let queue = dispatch_queue_create("my.bourne.testqueue",nil)
let queue = dispatch_queue_create("my.bourne.testqueue",DISPATCH_QUEUE_SERIAL)
//并行队列
let queue = dispatch_queue_create("my.bourne.testqueue", DISPATCH_QUEUE_CONCURRENT)
全局并行队列:这是系统提供的一个并发队列
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
3.创建任务
同步任务:会阻塞当前线程
dispatch_sync()
异步任务:不会阻塞当前线程
dispatch_async()
死锁:dispatch_sync 立即阻塞当前的主线程,然后把 Block 中的任务放到 main_queue 中,可是 main_queue 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以 Block 中的任务就不能完成,它不完成,dispatch_sync 就会一直阻塞主线程,这就是死锁现象。导致主线程一直卡死。
NSOperation NSOperationQueue
对GCD的封装,完全面向对象分别对应GCD中的任务和队列,执行的任务封装到一个NSOperation,再将任务添加到一个NSOperationQueue然后系统自动执行任务
NSOperation是一个抽象类,不能封装任务,它的两个子类可以封装任务,分别是NSInvocationOperation和NSBlockOpreation,前者是类型不安全,不建议使用
NSBlockOperation
let operationn = NSOperation({
in
//要执行代码。。。。
})
operation.start()
默认在当前线程执行,但是NSBlockOperation有个方法 addExecutionBlock通过这个方法可以给Operation 添加多个执行Block,block 会并发执行
主队列
多线程方案中都有一个主线程,这个特殊的线程必须串行,添加到主队列的任务都会一个接一个地排着队在主线程处理,通过单独的一个类方法来获得主队列
//
let queue = NSOperationQueue.mainQueue()
其他队列
通过初始化方法产生的队列就是其他队列了,因为只有这两种队列,除了主队列,其他队列就不需要名字了。其他队列的任务会在其他线程并行执行。
//创建其他队列
let queue = NSOperationQueue()
//创建NSBlockOperation对象
let operation = NSBlockOperation({
//.....执行的代码
})
queue.addOperation(operation)
与GCD队列比较,这里没有串行队列,如果要实现多个任务在其他线程串行,可以使用NSOperationQueue的参数 maxConCurrentOperationCount最大并发数,用来设置最多可以让多少个任务同时执行,当设置为1时,就是串行了;NSOperationQueue还有一个添加任务的方法,addOperationWithBlock({block}),添加一个任务到队列中,十分方便。NSOperation还有一个实用功能,就是添加依赖,如有三个任务,A:从服务器下载一张图片;B:给这张图片加个水印,C:把图片返回给服务器,这时候就用到依赖了.
//任务一:下载图片
let operation1 = NSBlockOperation({
NSLog(“下载图片 - %@”,NSThread.currentThread)
NSThread.sleepForTimeInterval(1.0)
})
//任务二:打水印
let operation2 = NSBlockOperation({
NSLog(“打水印 - %@”,NSThread.currentThread)
NSThread.sleepForTimeInterval(1.0)
})
//任务三:上传图片
let operation3 = NSBlockOperation({
NSLog("上传图片 - %@",NSThread.currentThread)
NSThread.sleepForTimeInterval(1.0)
})
//4.设置依赖
operation2.addDependency(operation1)//任务二依赖任务一
operation3.addDependency(operation2)//任务三依赖任务二
//5.创建队列并加入任务
let queue = NSOperationQueue()
queue.addOperations([operation3, operation2, operation1],waitUntilFinished: false)
注意:1.不能添加相互依赖,会死锁
2.可以使用removeDependency来解除依赖关系
3.可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没有关系。
还有一些常用的方法:
NSOperation
BOOL executing:判断任务是否在正在执行
BooL finished 判断任务是否完成
void(^completionBlock)(void)用来设置完成后需要执行的操作
cancel 取消任务
waitUntilFinished()
NSOperationQueue
NSUInterger operationCount 队列的任务数
cancelAllOperations取消队列中的所有任务
waitUntilAllOperationsAreFinished 阻塞当前线程直到此队列中的所有任务执行完毕
queue setSuspended true 暂停队列
queue setSuspended false 继续
线程同步:
所谓线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全问题,所采取的一种措施,有很多实现方法。
1.互斥锁:给需要同步的代码块添加一个互斥锁,就可以保证每次只有一个线程访问此代码块
objc_sync_enter(self)
//需要执行的代码块
objc_sync_exit(self)
2.同步执行:我们可以使用多线程的知识,把多个线程都要执行此段代码添加到同一个串行队列,这样就实现了线程同步的概念,这里可以使用GCD和NSOperation
GCD:
//需要一个全局变量queue,要让所有线程的这个操作都加到一个queue中
dispatch_sync(queue,^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInterval: 0.1];
NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
ticket -= 1
lastTicket = ticket
})
//NSOperation& NSOperationQueue
//重点: 1,全局的NSOperationQueue 所有的操作添加到同一个queue 中
2.设置queue的maxConCurrentOperationCount 为1
3.如果后续操作需要Block中的结果,就需要调用每个操作的waitUntilFinished,阻塞当前线程
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSInteger ticket = lastTicket;
[NSThread sleepForTimeInteval:1];
NSLog(@"%ld - %@", [NSThread currentThread]);
ticket -= 1;
lastTicket = ticket;
}]
[queue addOperation: operatin];
[operation waitUntilFinished];
//后续要做的事情
延迟执行
延迟一段时间再试行某段代码
perform
//3秒后自动调用self的run:方法,并且传递参数:@“abc”
[self performSelector :@selector(run:) withObject:@"abc", afterDelay: 3];
GCD
GCD中的disatch_after方法。
//创建队列
dispatch_queue_t queue = diapatch_get_global_queue(DISPATCH_PRIORITY_DEFAULT, 0);
//设置延时,单位秒
double delay = 3;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t)(delay* NESC_PER_SEC)), queue, ^{
//3秒后要执行的操作
})
NSTimer
[NSTimer scheduledTimerWithTimeInterval: 3.0 target: self selector:@selector(run:)]
单例模式
oc
@interface Tool:NSObject
+(instancetype) shareTool;
@end
@implementation Tool
static id_instance;
+(instancetype) shareTool {
static dispatch_once_t oneToken;
dispatch_once(&onceToken, ^{
_instance = [[Tool alloc] init];
})
return _instance;
}
@end
swift
class Tool: NSObject {
static let shareTool = Tool()
//私有化构造方法,阻止其他对象使用这个类的默认的“()”构造方法
private override init() {}
}
从其他线程回到主线程的方法
我们都知道在其他线程操作完成后必须要回到主线程更新UI,以下是一些方法回到主线程
NSThread
[self performSelectorOnMainThread:@selector(run) withObject: nil waitUnitilDone:NO];
GCD
//OC
dispatch_async(dispatch_get_main_queue, ^{
//....执行代码,刷新UI
})
//swift
dispatch_async(dispatch_get_main_queue, {
//执行刷新UI操作
})
NSOperationQueue
//oc
[[NSOperationQueue mainQueue] addOperationWithBlock:^ {
}];
//swift
NSOperationQueue.mianQueue().addOperationWithBlock{}