须要手动去管理线程的生命周期 可是他能够方便咱们知道当前线程是什么类的线程 可让咱们知道当前线程的各类属性 服务器
建立并启动多线程
先建立 再启动并发
// 建立 NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil]; // 启动 [thread start];
建立并自动启动异步
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
除了建立启动以外 NSThread还有不少方法 async
//取消线程 - (void)cancel; //启动线程 - (void)start; //判断某个线程的状态的属性 @property (readonly, getter=isExecuting) BOOL executing; @property (readonly, getter=isFinished) BOOL finished; @property (readonly, getter=isCancelled) BOOL cancelled; //设置和获取线程名字 -(void)setName:(NSString *)n; -(NSString *)name; //获取当前线程信息 + (NSThread *)currentThread; //获取主线程信息 + (NSThread *)mainThread; //使当前线程暂停一段时间,或者暂停到某个时刻 + (void)sleepForTimeInterval:(NSTimeInterval)time; + (void)sleepUntilDate:(NSDate *)date;
充分的利用了CPU 自动管理线程的生命周期 不须要咱们去完成 咱们只须要告诉他咱们想要作什么就行spa
GCD中有两个很是重要的概念 : 任务 和 队列线程
任务: 就是说明你想要作什么 说白了就是一段代码 在GCD中就是一段blockcode
任务有两种执行方式 同步执行 和异步执行orm
同步执行: 他会阻塞当前线程 等待Block中的任务执行完毕 而后当前线程才会继续往下运行.对象
异步执行:当前线程会直接往下执行,它不会阻塞当前线程
队列:用于存听任务。一共有两种队列 串行队列和并行队列
串行队列: 放到串行队列的任务 会取出来一个 执行一个 而后取下一个 这样一个一个的执行。
并行队列:取出来一个会放到别的线程,而后再取取出来再放到另外一个线程 这样因为取的动做很快 忽略不计 看起来全部的任务都是一块儿执行的
同步串行 : 当前线程 一个一个执行
同步并行 : 当前线程 一个一个执行
异步串行 : 其余线程 一个一个执行
异步并行 : 开不少线程 一块儿执行
主队列: 特殊的串行队列 用于刷新UI 任何须要刷新UI的工做都要在祝队列执行 因此通常耗时的任务都放在别的线程执行
本身建立的队列 : 串行队列和并行队列 有两个参数 第一个参数是一个标识符 第二个参数用来表示是串行的仍是并行的
//串行队列 dispatch_queue_t queue = dispatch_queue_create("my Queue", NULL); dispatch_queue_t queue = dispatch_queue_create("my Queue", DISPATCH_QUEUE_SERIAL); //并行队列 dispatch_queue_t queue = dispatch_queue_create("my Queue", DISPATCH_QUEUE_CONCURRENT);
全局并行队列:只要是并行任务通常都加入到这个队列 这是系统提供的一个并发队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
同步任务 : 会阻塞当前线程
dispatch_sync(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]); });
异步任务:不会阻塞当前线程
dispatch_async(<#queue#>, ^{ NSLog(@"%@", [NSThread currentThread]); });
为了更好的理解 看两个示例
如下代码在主线程调用 会是什么结果
NSLog(@"以前 - %@", [NSThread currentThread]); dispatch_sync(dispatch_get_main_queue(), ^{ NSLog(@"sync - %@", [NSThread currentThread]); }); NSLog(@"以后 - %@", [NSThread currentThread]);
答案:
只会打印第一句: 以前 - <NSThread: 0x7b3a9e16470>{number = 1, name = main},而后主线程就卡死了,你能够在界面上放一个按钮,你就会发现点不了了。
解释:
同步任务会阻塞当前线程,而后把 Block 中的任务放到指定的队列中执行,只有等到 Block 中的任务完成后才会让当前线程继续往下运行。
那么这里的步骤就是:打印完第一句后 dispath_sync 当即阻塞当前的主线程,而后把 Block 中的任务放到 main_queue 中,但是 main_queue 中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,因此 Block 中的任务就不能完成,它不完成 dispath_sync 就会一直阻塞主线程,这就是死锁现象。致使主线程一直卡死。
以前 - <NSThread: 0x7b3a9e16470>{number = 1, name = main}
如下代码会产生什么结果
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL); NSLog(@"以前 - %@", [NSThread currentThread]); dispatch_async(queue, ^{ NSLog(@"sync以前 - %@", [NSThread currentThread]); dispatch_sync(queue, ^{ NSLog(@"sync - %@", [NSThread currentThread]); }); NSLog(@"sync以后 - %@", [NSThread currentThread]); }); NSLog(@"以后 - %@", [NSThread currentThread]);
答案:
2016-12-26 13:25:31.689 ppp[41807:1430208] 以前 - <NSThread: 0x60800006ccc0>{number = 1, name = main}
2016-12-26 13:25:31.690 ppp[41807:1430208] 以后 - <NSThread: 0x60800006ccc0>{number = 1, name = main}
2016-12-26 13:25:31.690 ppp[41807:1430277] sync以前 - <NSThread: 0x608000261280>{number = 4, name = (null)}
很明显 sync - %@ 和sync以后 - %@ 没有被打印出来 崩溃了
分析:
咱们按执行顺序一步一步来:
1.使用 DISPATCH_QUEUE_SERIAL 这个参数,建立了一个串行队列
2.打印出 以前 - %@ 这句
3.dispatch_async 异步执行 因此当前线程不会被阻塞 就有了两条线程 一条当前线程继续往下打印出 以后 - %@ 这句 另外一条执行Block中的内容 打印 sync以前 - %@这句 由于这两条是并行的 因此打印前后顺序无所谓
4.如今的状况和上一个例子同样了 dispath_sync 同步执行 因而它所在的线程会被阻塞 一直等到 sync 里的任务执行完才会继续往下 因而 sync 就高兴的把本身 Block 中的任务放到 queue 中 但是 queue 是一个串行队列 一次执行一个任务 因此 sync 的 Block 必须等到前一个任务执行完毕 可没想到的是 queue 正在执行的任务就是被 sync 阻塞了的那个 因而又发生了死锁 因此 sync 所在的线程被卡死了 剩下的两句代码天然不会打印
NSOperation 是对GCD的封装 彻底面向对象 NSOperation和NSOperationQueue分别对应GCD的任务和队列.
1.将要执行的任务封装到一个NSOperation对象中
2.将此任务添加到一个NSOperationQueue对象中
系统会自动执行任务
NSOperation是一个抽象类 不能封装任务 能够用他的两个子类用于封装任务 分别是: NSInvocationOperation和NSBlockOperation 建立一个Operation后 须要用start方法来调用 它会默认在当前队列同步执行 也能够在中途取消一个任务 调用cancel方法.
NSInvocationOperation : 须要传入一个方法名。
//1.建立NSInvocationOperation对象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; //2.开始执行 [operation start];
NSBlockOperation
//1.建立NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //2.开始任务 [operation start];
以前说过这样的任务,默认会在当前线程执行。可是NSBlockOperation还有一个方法: addExecutionBlock:,经过这个方法能够给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务,注意下面的打印结果:
//1.建立NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //添加多个Block for (NSInteger i = 0; i < 5; i++) { [operation addExecutionBlock:^{ NSLog(@"第%ld次:%@", i, [NSThread currentThread]); }]; } //2.开始任务 [operation start];
打印输出
2016-12-26 13:43:04.015 ppp[42039:1438406] <NSThread: 0x60800006f540>{number = 1, name = main}
2016-12-26 13:43:04.015 ppp[42039:1438524] 第2次:<NSThread: 0x60800026ce00>{number = 5, name = (null)}
2016-12-26 13:43:04.015 ppp[42039:1438522] 第0次:<NSThread: 0x6000002622c0>{number = 3, name = (null)}
2016-12-26 13:43:04.015 ppp[42039:1438521] 第1次:<NSThread: 0x60000026ba80>{number = 4, name = (null)}
2016-12-26 13:43:04.016 ppp[42039:1438406] 第3次:<NSThread: 0x60800006f540>{number = 1, name = main}
2016-12-26 13:43:04.016 ppp[42039:1438524] 第4次:<NSThread: 0x60800026ce00>{number = 5, name = (null)}
NOTE: addExecutionBlock:方法必须在 start() 方法以前执行,不然就会报错:
‘*** -[NSBlockOperation addExecutionBlock:]: blocks cannot be added after the operation has started executing or finished'
主队列
NSOperationQueue *queue = [NSOperationQueue mainQueue];
其余队列
//1.建立一个其余队列 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; //2.建立NSBlockOperation对象 NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"%@", [NSThread currentThread]); }]; //3.添加多个Block for (NSInteger i = 0; i < 5; i++) { [operation addExecutionBlock:^{ NSLog(@"第%ld次:%@", i, [NSThread currentThread]); }]; } //4.队列添加任务 [queue addOperation:operation];
打印输出
2016-12-26 13:48:55.611 ppp[42100:1441056] 第1次:<NSThread: 0x608000462240>{number = 6, name = (null)}
2016-12-26 13:48:55.611 ppp[42100:1441055] <NSThread: 0x600000261800>{number = 3, name = (null)}
2016-12-26 13:48:55.611 ppp[42100:1441067] 第2次:<NSThread: 0x60800027a740>{number = 4, name = (null)}
2016-12-26 13:48:55.611 ppp[42100:1441058] 第0次:<NSThread: 0x608000462180>{number = 5, name = (null)}
2016-12-26 13:48:55.612 ppp[42100:1441056] 第3次:<NSThread: 0x608000462240>{number = 6, name = (null)}
2016-12-26 13:48:55.612 ppp[42100:1441055] 第4次:<NSThread: 0x600000261800>{number = 3, name = (null)}
没有串行队列?
NSOperationQueue有一个参数 maxConcurrentOperationCount 最大并发数,用来设置最多可让多少个任务同时执行。当你把它设置为 1
的时候,他就是串行!
NSOperationQueue还有一个添加任务的方法- (void)addOperationWithBlock:(void (^)(void))block; 这样能够添加一个任务到队列中 十分方便
NSOperation有一个很是实用的功能 那就是添加依赖 好比有 3 个任务 A: 从服务器上下载一张图片 B:给这张图片加个水印 C:把图片返回给服务器。这时就能够用到依赖了.
//1.任务一:下载图片 NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"下载图片 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; }]; //2.任务二:打水印 NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"打水印 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; }]; //3.任务三:上传图片 NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"上传图片 - %@", [NSThread currentThread]); [NSThread sleepForTimeInterval:1.0]; }]; //4.设置依赖 [operation2 addDependency:operation1]; //任务二依赖任务一 [operation3 addDependency:operation2]; //任务三依赖任务二 //5.建立队列并加入任务 NSOperationQueue *queue = [[NSOperationQueue alloc] init]; [queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];
打印结果
2016-12-26 13:54:33.953 ppp[42218:1444656] 下载图片 - <NSThread: 0x60800007ac00>{number = 3, name = (null)}
2016-12-26 13:54:35.028 ppp[42218:1444659] 打水印 - <NSThread: 0x60800007f280>{number = 4, name = (null)}
2016-12-26 13:54:36.103 ppp[42218:1444659] 上传图片 - <NSThread: 0x60800007f280>{number = 4, name = (null)}
注意: 不能添加相互依赖 会死锁 好比A依赖B B依赖A
可使用 removeDependency 来解除依赖关系
能够在不一样的队列之间依赖 反正就是这个依赖是添加到任务身上的 和队列不要紧
还有一些方法
NSOperation
BOOL executing; //判断任务是否正在执行
BOOL finished; //判断任务是否完成
void (^completionBlock)(void); //用来设置完成后须要执行的操做
- (void)cancel; //取消任务
- (void)waitUntilFinished; //阻塞当前线程直到此任务执行完毕
NSOperationQueue
NSUInteger operationCount; //获取队列的任务数
- (void)cancelAllOperations; //取消队列中全部的任务
- (void)waitUntilAllOperationsAreFinished; //阻塞当前线程直到此队列中的全部任务执行完毕
[queue setSuspended:YES]; // 暂停queue
[queue setSuspended:NO]; // 继续queue
NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
GCD
dispatch_async(dispatch_get_main_queue(), ^{ });
NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ }];
好啦 就这么多啦 可能写的也不是特别的好 多线程也不止这么些东西 谢谢你们