iOS多线程总结

1.一些概念

·进程:正在运行的可执行文件,拥有独立的虚拟内存空间和系统资源。包含一个主线程和多个子线程,若主线程退出,则该进程结束。
·线程:一个独立的、最小代码执行路径。iOS线程底层基于POSIX。
·任务:一段代码。
·串行和并行区别:允许同时执行的任务的数量,串行一次执行一个任务,而并行一次允许一次执行多个任务。
·同步和异步的区别:是否阻塞当前线程。同步阻塞,等待上一次任务的结果。异步不阻塞。
·队列和线程:串行队列一次执行一个任务,并发队列同时执行多个任务,iOS利用队列对任务进行调度,根据调度需要和系统当前负载状态创建、销毁线程,不需要手动管理线程。
在iOS中,系统与线程打交道,而我们只需定义好要调度的任务。


2.GCD

2.1
· 四个概念:
·同步 (sync):阻塞当前线程,直到block中的任务执行完毕。
·异步 (async):不阻塞当前线程,新开线程
·串行队列:遵循FIFO                 

·并行队列


147286-ffbb612b8d64d9a1.png

2.2创建队列
1)主队列
用来刷新UI,注意耗时操作不要放到主线程执行

dispatch_queue_t queue = dispatch_get_main_queue();

2)自定义队列 (串行,并行)

// 第二个参数传 DISPATCH_QUEUE_SERIAL 或 NULL 为串行,传DISPATCH_QUEUE_CONCURRENT为并行)
dispatch_queue_t queue = dispatch_queue_create("com.baidu.wk.testQueue", NULL);

3)全局并行队列

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

2.3 创建任务
1)同步任务(不开线程)

dispatch_sync(, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });

2)异步线程(开线程)

dispatch_async(, ^{
      //code here
      NSLog(@"%@", [NSThread currentThread]);
  });

2.4 组队列
把多个队列加入一个组,整体执行完毕发出通知
dispatch_group_create

dispatch_group_async

dispatch_group_notify

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

2.5 延迟操作 dispatch_after

 dispatch_after(dispatch_time_t when,  dispatch_queue_t queue, ^{
      
  });

2.6 执行一次dispatch_after
一次性,用于单例设计模式,是线程安全的。性能比互斥锁高很多,apple推荐使用。
2.7 线程间通讯
在全局队列中异步执行耗时操作,之后将结果传给主线程,用于UI展示

dispatch_async(dispatch_get_global_queue(0,0), ^{
  dispatch_async(dispatch_get_main_queue(), ^{
    // 更新 UI

  });
});
  1. NSOperation
    NSOperation和NSOperationQueue 分别对应GCD的任务和队列,步骤如下:
    · 将要执行的任务封装到一个NSOperation 对象中。
    · 将此任务添加到一个NSOperationQueue 对象中。
    · NSOperation 为抽象类,有子类NSInvocationOperation 和 NSBlockOperation,只有异步没有同步。
    · NSInvocationOperation:调用某selector;
    NSBlockOperation: 并发执行一个或多个block
  2. 添加任务
    3.1.1 NSInvocationOperation
//1.创建NSInvocationOperation对象 NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil]; 
//2.开始执行 [operation start];

3.1.2 NSBlockOperation

//1.创建NSBlockOperation对象
  NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
      NSLog(@"%@", [NSThread currentThread]);
  }];
  //2.开始任务
  [operation start];

addExecutionBlock: 通过这个方法可以给 Operation 添加多个并发执行的Block,会在主线程和其它的多个线程执行这些任务。类似 dispatch_group.

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

3.1.3 自定义operation (TODO)
要继承 NSOperation 类,并实现其 main() 方法,还需要实现 cancel() 在内的各种方法。
暂没用过,待补充。
3.2 将任务加入对列
· addOperation:添加一个operation 到 operation queue中
· addOperations:waitUntiFinished:添加一组operations到 operation queque 中
· addOperationWithBlock: 直接添加一个 block 到 operation queue 中,而不用创建一个 NSBlockOperation 对象。
1)

NSOperationQueue *queue = [NSOperationQueue mainQueue];
  1. 其他队列
/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];

3)设置最大并发数
思考一个问题,maxConcurrentOperationCount = 1 与 GCD 串行执行是一样的吗?

maxConcurrentOperationCount > 1就是并发, = 1是串行的效果,但 operation 的执行顺序还是一样会受其他因素影响的,比如 operation 的 isReady 状态、operation 的队列优先级等。

因此,一个串行的 operation queue 与一个串行的 dispatch queue 还是有本质区别的,因为 dispatch queue 的执行顺序一直是 FIFO 的。如果 operation 的执行顺序对我们来说非常重要,那么我们就应该在将 operation 添加到 operation queue 之前就建立好它的依赖关系。
3.3 添加依赖
-addOperations:waitUntileFinished: 这个是异步,多操作完成后后续操作。
-addDependency: 依赖关系,弥补NSOperation异步无法保证执行顺序。互相依赖会导致死锁。但可以跨队列依赖。

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

3.4 其他用法
· 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

4 线程同步
线程同步就是为了防止多个线程抢夺同一个资源造成的数据安全的问题,所采取的一种措施
· 互斥锁 :给需要同步的代码块加一个互斥锁,就可以保证每次只有一个线程访问此代码块。

@synchronized(self) {
    //需要执行的代码块
}

· 同步执行:我们可以使用多线程的知识,把多个线程都要执行此段代码添加到一个串行队列,这样就实现了线程同步的概念。当然这里可以使用GCD和NSOpretion两种方案

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

. 延迟执行

[self  performSelector:@selector(run:) withObject:@"abc" afterDelay:3];
dispatch_after
[NSTimer scheduledTimerWithTimeInterval:3.0 target:self selector:@selector(run:) userInfo:@"abc" repeats:NO];

. 单例模式

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

. GCD & NSOperation对比
NSOperation: 对 GCD 的封装,面向对象,OC,特性:operation 间依赖;
取消一个正在执行的 operation , 暂停和恢复 operation queue 等;
NSOperationQueue支持KVO,可以监测operation状态,例如,是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld);
设置优先级;
GCD:C语言,轻量级,效率更高。一次性、组队列、延时。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 201,681评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,710评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,623评论 0 334
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,202评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,232评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,368评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,795评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,461评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,647评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,476评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,525评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,226评论 3 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,785评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,857评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,090评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,647评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,215评论 2 341

推荐阅读更多精彩内容

  • 目录 简述 NSThread GCD操作与队列异步操作并行队列同步操作并行队列同步操作串行队列异步操作串行队列队列...
    鱼王00阅读 490评论 0 2
  • NSThread 第一种:通过NSThread的对象方法 NSThread *thread = [[NSThrea...
    攻城狮GG阅读 780评论 0 3
  • 进程 什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内...
    45b645c5912e阅读 465评论 0 5
  • 在这篇文章中,我将为你整理一下 iOS 开发中几种多线程方案,以及其使用方法和注意事项。当然也会给出几种多线程的案...
    张战威ican阅读 598评论 0 0
  • object的每个实例都有下列属性和方法 constructor 保存着用于创建当前对象的函数。对于前面的例子而言...
    他在发呆阅读 132评论 0 0