多线程

作者: 火焰与柠檬 | 来源:发表于2016-07-07 00:03 被阅读0次

iOS中的多线程解决方案:

  • Pthreads
  • NSThread
  • GCD
  • NSOperation & NSOperationQueue

Pthreads

基于C语言,在类Unix操作系统(Unix、Linux、Mac OS X等)中,都可以使用。

(1)实现必须导入头文件

#import <pthread.h>

(2)然后创建线程,并执行任务

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    pthread_t thread;
    //创建一个线程并自动执行
    pthread_create(&thread, NULL, start, NULL);
}

void *start(void *data) {
    NSLog(@"%@", [NSThread currentThread]);

    return NULL;
}

由于是C语言函数,所以需要自己管理线程的生命周期(切换、销毁)

NSThread

Apple封装之后,面向对象,但是仍然需要手动管理生命周期。但可以获得当前线程的各种属性,比[NSThread currentThread],用于调试很方便。

  • 创建线程,然后开启线程
  // 创建
  NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];

  // 启动
  [thread start];
  • 创建并自动开启线程
  [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
  • performSelector形式创建线程
  [self performSelectorInBackground:@selector(run:) withObject:nil];
其他作用
//取消线程
- (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

使用了Block,系统自动管理生命周期。

  • 任务:每一个Block就是一个任务(你想要做什么)。
  • 队列:用于存放任务。
任务有两种执行方式:

同步执行:会阻塞当前线程,必须等Block完,才能继续往下执行。

异步执行:不会阻塞当前线程,程序不必等待,会直接继续执行。

队列也有两种:

串行队列:按顺序执行,即先进入的先执行,后进入的后执行。

并行队列:同时执行任务,没有执行顺序。

注意:异步操作才会开辟新线程。串行队列按顺序执行任务,并行队列同时执行多个任务。

创建队列
  • 主队列:不会开辟新线程,串行执行任务。
  dispatch_queue_t queue = ispatch_get_main_queue();
  • 全局队列:并行队列。
  dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  • 创建串行和并行队列。
  //串行队列
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
  //并行队列
  dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
创建任务
  • 同步
  dispatch_sync(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });
  • 异步
  dispatch_async(<#queue#>, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });
总结效果:
各个队列总结表

注意:
异步添加串行队列,只会开启一个新线程;

异步和同步决定是否开启新线程,并行和串行决定任务的执行顺序;

队列组

队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们

//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue, ^{
    for (NSInteger i = 0; i < 3; i++) {
        NSLog(@"group-01 - %@", [NSThread currentThread]);
    }
});

//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue(), ^{
    for (NSInteger i = 0; i < 8; i++) {
        NSLog(@"group-02 - %@", [NSThread currentThread]);
    }
});

//3.3.执行5次循环
dispatch_group_async(group, queue, ^{
    for (NSInteger i = 0; i < 5; i++) {
        NSLog(@"group-03 - %@", [NSThread currentThread]);
    }
});

//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
    NSLog(@"完成 - %@", [NSThread currentThread]);
});

NSOperation和NSOperationQueue

NSOperation 是苹果公司对 GCD 的封装,完全面向对象,所以使用起来更好理解。 大家可以看到NSOperationNSOperationQueue 分别对应 GCD 的 任务 和 队列 。

1.将要执行的任务封装到一个 NSOperation 对象中。

2.将此任务添加到一个 NSOperationQueue 对象中。

添加任务

NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperationNSBlockOperation 。创建一个 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];
创建队列

看过上面的内容就知道,我们可以调用一个 NSOperation 对象的 start() 方法来启动这个任务,但是这样做他们默认是 同步执行 的。就算是 addExecutionBlock 方法,也会在当前线程和其他线程中执行,也就是说还是会占用当前线程。这时就要用到队列 NSOperationQueue了。而且,按类型来说的话一共有两种类型:主队列、其他队列。只要添加到队列,会自动调用任务的 start() 方法。

  • 主队列
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];
  • NSOperationQueue 有一个参数maxConcurrentOperationCount (最大并发数),用来设置最多可以让多少个任务同时执行。当你把它设置为 1 的时候,就是串行。

  • 添加任务的方法:- (void)addOperationWithBlock:(void (^)(void))block;

  • 添加依赖

//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];

注意:

(1)不能添加相互依赖,会死锁,比如 A依赖B,B依赖A。

(2)可以使用 removeDependency 来解除依赖关系。

(3)可以在不同的队列之间依赖,反正就是这个依赖是添加到任务身上的,和队列没关系。

其他方法
  • 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

其他

线程同步
  • 同步锁,即资源竞争:多条线程同时访问同一资源,造成的数据安全问题,通过加锁保证每次只有一个线程访问资源。
@synchronized(self) {
    //需要执行的代码块
}
  • 同步执行:把多个线程都要执行此段代码添加到同一个串行队列,这样就实现了线程同步的概念。
//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,阻塞当前线程,一直等到当前操作完成,才允许执行后面的。waitUntilFinished 要在添加到队列之后!

  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSInteger ticket = lastTicket;
      [NSThread sleepForTimeInterval:1];
      NSLog(@"%ld - %@",ticket, [NSThread currentThread]);
      ticket -= 1;
      lastTicket = ticket;
  }];

  [queue addOperation:operation];

  [operation waitUntilFinished];

  //后续要做的事
延迟执行
  • perform
  // 3秒后自动调用self的run:方法,并且传递参数:@"abc"
  [self performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
  • GCD
// 创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
// 设置延时,单位秒
double delay = 3; 

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delay * NSEC_PER_SEC)), queue, ^{
    // 3秒后需要执行的任务
});
  • NSTimer
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];
单例
@interface Tool : NSObject <NSCopying>

+ (instancetype)sharedTool;

@end

@implementation Tool

static id _instance;

+ (instancetype)sharedTool {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[Tool alloc] init];
    });

    return _instance;
}

@end
从其他线程返回主线程
  • NSThread
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:NO];
  • GCD
dispatch_async(dispatch_get_main_queue(), ^{

});
  • NSOperationQueue
[[NSOperationQueue mainQueue] addOperationWithBlock:^{

}];
几道关于线程死锁的例题

多线程死锁例题

相关文章

  • iOS多线程 NSOperation

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程 pthread、NSThread

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程: GCD

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程运用

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • iOS多线程基础

    系列文章: 多线程 多线程 pthread、NSThread 多线程 GCD 多线程 NSOperation 多线...

  • 多线程介绍

    一、进程与线程 进程介绍 线程介绍 线程的串行 二、多线程 多线程介绍 多线程原理 多线程的优缺点 多线程优点: ...

  • iOS进阶之多线程管理(GCD、RunLoop、pthread、

    深入理解RunLoopiOS多线程--彻底学会多线程之『GCD』iOS多线程--彻底学会多线程之『pthread、...

  • iOS多线程相关面试题

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • 多线程之--NSOperation

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • iOS多线程之--NSThread

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

网友评论

      本文标题:多线程

      本文链接:https://www.haomeiwen.com/subject/chzkjttx.html