文档章节

iOS中的多线程

KevinEmily
 KevinEmily
发布于 2016/12/26 14:04
字数 2689
阅读 9
收藏 0

Nsthread  

需要手动去管理线程的生命周期    但是他可以方便我们知道当前线程是什么类的线程 可以让我们知道当前线程的各种属性 

创建并启动

先创建 再启动

  // 创建
  NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

  // 启动
  [thread start];

创建并自动启动

[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];

除了创建启动之外 NSThread还有很多方法 

//取消线程
- (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;

GCD

充分的利用了CPU  自动管理线程的生命周期  不需要我们去完成  我们只需要告诉他我们想要做什么就行

GCD中有两个非常重要的概念 : 任务 和 队列

任务:  就是说明你想要做什么  说白了就是一段代码  在GCD中就是一段block

任务有两种执行方式   同步执行 和异步执行

同步执行: 他会阻塞当前线程 等待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

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:^{

}];

好啦  就这么多啦  可能写的也不是特别的好 多线程也不止这么些东西  谢谢大家

© 著作权归作者所有

下一篇: 清除缓存
KevinEmily
粉丝 18
博文 40
码字总数 23590
作品 0
东城
私信 提问

暂无文章

Excption与Error包结构,OOM 你遇到过哪些情况,SOF 你遇到过哪些情况

Throwable 是 Java 中所有错误与异常的超类,Throwable 包含两个子类,Error 与 Exception 。用于指示发生了异常情况。 Java 抛出的 Throwable 可以分成三种类型。 被检查异常(checked Exc...

Garphy
今天
5
0
计算机实现原理专题--二进制减法器(二)

在计算机实现原理专题--二进制减法器(一)中说明了基本原理,现准备说明如何来实现。 首先第一步255-b运算相当于对b进行按位取反,因此可将8个非门组成如下图的形式: 由于每次做减法时,我...

FAT_mt
昨天
6
0
好程序员大数据学习路线分享函数+map映射+元祖

好程序员大数据学习路线分享函数+map映射+元祖,大数据各个平台上的语言实现 hadoop 由java实现,2003年至今,三大块:数据处理,数据存储,数据计算 存储: hbase --> 数据成表 处理: hive --> 数...

好程序员官方
昨天
7
0
tabel 中含有复选框的列 数据理解

1、el-ui中实现某一列为复选框 实现多选非常简单: 手动添加一个el-table-column,设type属性为selction即可; 2、@selection-change事件:选项发生勾选状态变化时触发该事件 <el-table @sel...

everthing
昨天
6
0
【技术分享】TestFlight测试的流程文档

上架基本需求资料 1、苹果开发者账号(如还没账号先申请-苹果开发者账号申请教程) 2、开发好的APP 通过本篇教程,可以学习到ios证书申请和打包ipa上传到appstoreconnect.apple.com进行TestF...

qtb999
昨天
10
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部