iOS与多线程(四) —— NSOperation的串并行和操作依赖

版本记录

版本号 时间
V1.0 2017.08.16

前言

信号量机制是多线程通信中的比较重要的一部分,对于NSOperation可以设置并发数,但是对于GCD就不能设置并发数了,那么就只能靠信号量机制了。接下来这几篇就会详细的说一下并发机制。感兴趣的可以看这几篇文章。
1. iOS与多线程(一) —— GCD中的信号量及几个重要函数
2. iOS与多线程(二) —— NSOperation实现多并发之创建任务
3. iOS与多线程(三) —— NSOperation实现多并发之创建队列和开启线程

NSOperation串并行的实现

NSOperation的串并行是通过设置最大并发数maxConcurrentOperationCount来实现的。

  • maxConcurrentOperationCount为1时,就是串行执行
  • maxConcurrentOperationCount > 1时,就是并行执行

不管是串行还是并行,开启的线程数目是系统决定的,并不是我们决定的。

1. 串行

下面我们看一下代码。

#import "JJSerialConquenceVC.h"

@interface JJSerialConquenceVC ()

@end

@implementation JJSerialConquenceVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //设置并发数
    queue.maxConcurrentOperationCount = 1;
    
    [queue addOperationWithBlock:^{
        NSLog(@"1_thread = %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"2_thread = %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"3_thread = %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"4_thread = %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"5_thread = %@",[NSThread currentThread]);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"6_thread = %@",[NSThread currentThread]);
    }];
}

@end

下面看输出结果

2017-08-16 17:58:58.090660+0800 JJOC[10149:4513595] 1_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
2017-08-16 17:58:58.090813+0800 JJOC[10149:4513595] 2_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
2017-08-16 17:58:58.090868+0800 JJOC[10149:4513595] 3_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
2017-08-16 17:58:58.090919+0800 JJOC[10149:4513595] 4_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
2017-08-16 17:58:58.090967+0800 JJOC[10149:4513595] 5_thread = <NSThread: 0x170276740>{number = 4, name = (null)}
2017-08-16 17:58:58.091014+0800 JJOC[10149:4513595] 6_thread = <NSThread: 0x170276740>{number = 4, name = (null)}

可见,是串行执行的,而且系统只给开了一个线程。

2. 并行

下面我们看一下并行下的代码。

#import "JJSerialConquenceVC.h"

@interface JJSerialConquenceVC ()

@end

@implementation JJSerialConquenceVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.view.backgroundColor = [UIColor whiteColor];
    
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    //设置并发数
    queue.maxConcurrentOperationCount = 3;
    
    [queue addOperationWithBlock:^{
        NSLog(@"1_thread = %@",[NSThread currentThread]);
        sleep(1);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"2_thread = %@",[NSThread currentThread]);
        sleep(1);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"3_thread = %@",[NSThread currentThread]);
        sleep(1);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"4_thread = %@",[NSThread currentThread]);
        sleep(1);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"5_thread = %@",[NSThread currentThread]);
        sleep(1);
    }];
    
    [queue addOperationWithBlock:^{
        NSLog(@"6_thread = %@",[NSThread currentThread]);
        sleep(1);
    }];
}

@end

看输出结果

2017-08-16 18:02:38.893083+0800 JJOC[10154:4514418] 1_thread = <NSThread: 0x1742716c0>{number = 4, name = (null)}
2017-08-16 18:02:38.893199+0800 JJOC[10154:4514414] 2_thread = <NSThread: 0x1700746c0>{number = 3, name = (null)}
2017-08-16 18:02:38.893644+0800 JJOC[10154:4514415] 3_thread = <NSThread: 0x174271840>{number = 5, name = (null)}
2017-08-16 18:02:39.897629+0800 JJOC[10154:4514414] 5_thread = <NSThread: 0x1700746c0>{number = 3, name = (null)}
2017-08-16 18:02:39.897613+0800 JJOC[10154:4514418] 4_thread = <NSThread: 0x1742716c0>{number = 4, name = (null)}
2017-08-16 18:02:39.898884+0800 JJOC[10154:4514415] 6_thread = <NSThread: 0x174271840>{number = 5, name = (null)}

可以看见系统给开启了3个线程,执行顺序也变成了123546,说明是并行的。


操作依赖

有的时候我们希望执行完一个任务再去执行另外一个任务,这个时候就需要操作依赖了。比如说A、B两个操作,我们希望执行完A操作以后再去执行B操作,那么我们就可以设置B依赖于A,具体就是使用方法addDependency:

下面还是直接看代码。

#import "JJAddDependencyVC.h"

@interface JJAddDependencyVC ()

@end

@implementation JJAddDependencyVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1_thread = %@",[NSThread currentThread]);
        
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"2_thread = %@",[NSThread currentThread]);
        
    }];
    
    [operation2 addDependency:operation1];
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

@end

下面看输出结果

2017-08-16 18:15:05.030277+0800 JJOC[10159:4515800] 1_thread = <NSThread: 0x17007d5c0>{number = 4, name = (null)}
2017-08-16 18:15:05.030469+0800 JJOC[10159:4515800] 2_thread = <NSThread: 0x17007d5c0>{number = 4, name = (null)}

可见,通过操作依赖实现了运行顺序的控制。

下面说一点题外话,就是GCD中线程同步的实现。

group实现

#import "JJGCDGroupVC.h"

@interface JJGCDGroupVC ()

@end

@implementation JJGCDGroupVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    dispatch_queue_t queue = dispatch_queue_create(0, 0);
    dispatch_group_t group = dispatch_group_create();
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        NSLog(@"task 1 on %@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_enter(group);
    dispatch_group_async(group, queue, ^{
        NSLog(@"task 2 on %@",[NSThread currentThread]);
        dispatch_group_leave(group);
    });
    
    dispatch_group_notify( group, queue, ^{
        NSLog( @"all task done %@", [NSThread currentThread] );
        NSLog(@"可以执行其他的任务了");
    } );
}

@end

下面看输出结果

2017-08-16 18:35:32.894210+0800 JJOC[10163:4518939] task 1 on <NSThread: 0x174079900>{number = 4, name = (null)}
2017-08-16 18:35:32.894343+0800 JJOC[10163:4518939] task 2 on <NSThread: 0x174079900>{number = 4, name = (null)}
2017-08-16 18:35:32.894393+0800 JJOC[10163:4518939] all task done <NSThread: 0x174079900>{number = 4, name = (null)}
2017-08-16 18:35:32.894424+0800 JJOC[10163:4518939] 可以执行其他的任务了

可见开启了线程并实现了任务的同步。

信号量semaphore实现

    dispatch_semaphore_t sem =     dispatch_semaphore_create(0);
    [networkManager requestWithDelay:5 completion:^{
        dispatch_semaphore_signal(sem);//+1
    }];
    dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//-1
    NSLog(@"OK");

信号量方面的知识,前几篇已经说过了,这里很清晰的是等待网络请求结束,才可以行NSLog(@"OK");这段代码。

barrier

barrier阻塞的使用起来就很方便了,它就相当于一个栅栏,将不同的任务块区分开来,如下图所示。

barrier

这里需要主要的是,barrier只能用于并发队列不能用于全局队列。

下面看代码。

#import "JJGCDBarrierVC.h"

@interface JJGCDBarrierVC ()

@end

@implementation JJGCDBarrierVC

#pragma mark - Override Base Function

- (void)viewDidLoad
{
    [super viewDidLoad];
    
    //创建并发队列
    dispatch_queue_t queue = dispatch_queue_create("cc.imguiqing", DISPATCH_QUEUE_CONCURRENT);
    
    //开启异步任务
    
    dispatch_async(queue, ^{
        NSLog(@"task 1 on %@",[NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task 2 on %@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier ==========");
    });
    
    dispatch_async(queue, ^{
        NSLog(@"task 3 on %@",[NSThread currentThread]);
    });
}

@end

下面看输出结果

2017-08-16 18:49:07.271659+0800 JJOC[10166:4521475] task 1 on <NSThread: 0x174078380>{number = 3, name = (null)}
2017-08-16 18:49:07.271736+0800 JJOC[10166:4521478] task 2 on <NSThread: 0x170261b80>{number = 4, name = (null)}
2017-08-16 18:49:07.271761+0800 JJOC[10166:4521478] barrier ==========
2017-08-16 18:49:07.271799+0800 JJOC[10166:4521478] task 3 on <NSThread: 0x170261b80>{number = 4, name = (null)}

这个很好理解就不多说了。


一些其他方法

1. - (void)cancel;

  • NSOperation类中的方法,取消单个操作,这里的取消不是立即取消操作,而是当前的操作完成以后不再继续执行新的操作。

2. - (void)cancelAllOperations;

  • NSOperationQueue中的方法,取消队列中的所有操作。

3. @property (getter=isSuspended) BOOL suspended;

  • NSOperationQueue中只读属性,用来判断队列是否暂停。

上面所说的名词,暂停和取消是有区别的,暂停的意思就是操作以后还可以回复,而取消的意思就是所有的操作清空,不能继续执行其他的剩下的操作了。

后记

未完,待续~~~

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

推荐阅读更多精彩内容