iOS中的多线程

Nsthread  

须要手动去管理线程的生命周期    可是他能够方便咱们知道当前线程是什么类的线程 可让咱们知道当前线程的各类属性 服务器

建立并启动多线程

先建立 再启动并发

// 建立
  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;

GCD

充分的利用了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

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

}];

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

相关文章
相关标签/搜索