iOS多线程讨论

马云催泪励志演讲 为什么你还是穷人

开发中为了更好的用户体验我们会用到多线程。

主要讨论三中创建多线程的方法:NSThread,GCD,NSOperation 。

NSThread

从命名来看这是一个封装好的类,它的生命周期需要我们手动管理。常用的创建方法

1、通过类方法创建并自动启动:

+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;

2、通过实例方法创建手动启动:

NSThread *newThread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];

[newThread setName:@"threadName"];//自定义线程名称

[newThread start];//启动

[newThread cancel];//取消

关于NSThread我们实际开发中常用的方法是:

+ (NSThread *)currentThread;//获取当前线程信息

+ (NSThread *)mainThread;//获取主线程信息

+ (void)sleepForTimeInterval:(NSTimeInterval)time;//调试时用的睡眠

GCD

GCD为Grand Central Dispatch首字母缩写,是Apple开发的一个多核编程的解决方案。起源于Mac OS X 10.6,在iOS4.0被引入。GCD会自动管理线程的生命周期,采用C语言实现,通过Block将执行体传入。

1、GCD中有三种队列类型:

①the main queue:

获取方式dispatch_queue_t mainQueue = dispatch_get_main_queue();提交到该队列中的任务会在主线程执行,为一个串行队列。

②the global queue:

获取方式    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);全局队列是并发队列,并由整个进程共享,第一个参数为选择队列,参数可选为

#define DISPATCH_QUEUE_PRIORITY_HIGH 2//高

#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0//中,默认

#define DISPATCH_QUEUE_PRIORITY_LOW (-2)//低

#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN//后台

③the user defined queue:

获取方式:第一个参数为名称,debug时会显示

dispatch_queue_t serialQueue =dispatch_queue_create("com.serial.queue",NULL);//串行

dispatch_queue_t serialQueue = dispatch_queue_create("com.serial.queue", DISPATCH_QUEUE_SERIAL);//串行

dispatch_queue_t concurrentQueue = dispatch_queue_create("com.concurrent.queue", DISPATCH_QUEUE_CONCURRENT);//并行

2、应用:

①关于dispatch_async---添加任务到一个队列并不等待任务完成,而是立即继续其他任务。

//首先相对当前语句所在的线程来说是异步提交,将任务提交到default级别的全局队列中返回,block等待default队列FIFO顺序执行

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{

[self goDoSomethingLongTask];//在default队列中执行完,此时不会继续往下执行,除非当前语句块执行完成

dispatch_async(dispatch_get_main_queue(), ^{//异步提交任务到main_queue

[textField setStringValue:@"Done doing something long and involved"];//在main中执行当前语句块

});

});

②关于dispatch_sync---添加任务到一个队列并等待直到任务完成,用于等待提交任务的结果,才能继续执行下面的代码块的情况。

dispatch_sync一般应用在并发队列:这才是做同步工作的好选择。

在主线程中执行如下代码块:

//首先相对当前语句所在的线程来说是同步提交,将任务提交到main queue中,等待main queue执行该block-----这里会造成阻塞,因为sync已经阻塞当前线程,等待block在所提交的线程执行完成后才放开阻塞的线程,即A被阻塞等待任务执行完,任务又等A的调度执行。

dispatch_sync(dispatch_get_main_queue(), ^{

NSLog("sync - %@", NSThread.currentThread());

});

③关于dispatch_group

演示一个应用:

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);//获得一个异步队列

dispatch_group_t group = dispatch_group_create();//新建一个group队列

for(idobj in array){//for循环遍历 每次异步方式提交任务block到queue,

dispatch_group_async(group, queue, ^{

[selfdoSomethingIntensiveWithbj];

});

}

dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//group进入永久阻塞等待,直到之前任务完成

dispatch_release(group);

[selfdoSomethingWith:array];//group完成后,执行此

优化:将最后的任务放到异步线程中执行

dispatch_queue_t queue = dispatch_get_global_qeueue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);

dispatch_group_t group = dispatch_group_create();

for(idobj in array){//近乎平行执行

dispatch_group_async(group, queue, ^{

[selfdoSomethingIntensiveWithbj];

});

}

//之前任务完成后会被notify通知执行

dispatch_group_notify(group, queue, ^{

[selfdoSomethingWith:array];

});

dispatch_release(group);

④关于dispatch_apply。

同步执行dispath_apply代码块,调用单一block多次,并平行运算,然后等待所有运算结束,需保证block中线程安全

dispatch_apply(array.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) {

[NSThread sleepForTimeInterval:1];

NSLog(@"---%@",array[index]);

});

[self say];//最后执行

⑤关于dispatch_barrier。在并发队列中扮演一个串行式的瓶颈作用。

能够确保提交的block在被执行的特定时间上是指定队列上唯一被执行的条目!由于队列是FIFO的,换句话说所有先于该barrier block的条目一定能再这个block执行前完成。等完成该block后队列恢复默认状态。

应用--读写问题:

线程不能保证安全

- (void)addPhoto:(Photo *)photo

{

if (photo) {

[_photosArray addObject:photo];

dispatch_async(dispatch_get_main_queue(), ^{

[self postContentAddedNotification];

}

修改成一个线程安全的写操作

-(void)addPhoto:(Photo *)photo

{

if (photo) { // 1判断非空

dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2 barrier方式异步提交代码块到queue中

[_photosArray addObject:photo]; // 3等之前提交的都执行完后,只有当前代码块在queue中执行,barrier提交确保线程安全的!

dispatch_async(dispatch_get_main_queue(), ^{ // 4通知

[self postContentAddedNotification];

});

});

}

}

读操作

不能提供任何保护来对抗当一个线程调用读方法的同时另一个线程调用写方法

- (NSArray *)photos

{

return [NSArray arrayWithArray:_photosArray];

}

修改为:

- (NSArray *)photos

{

__block NSArray *array; // 1

dispatch_sync(self.concurrentPhotoQueue, ^{ // 2同步提交等待,block代码块在queue中执行那个完成,因为写操作是barrier提交保证了执行时只有写操作自己在queue中!

array = [NSArray arrayWithArray:_photosArray]; // 3等待queue调度执行代码块

});

return array;

}

⑥dispatch_once,常用于创建线程安全的单例。

+ (TransHistoryInstance *)sharedInstance

{

static TransHistoryInstance *instance;

static dispatch_once_t onceToken;

dispatch_once(&onceToken, ^{

instance = [[self alloc] init];

});

return instance;

}

⑦dispatch_after  用于延迟操作

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{

//code to be executed after a specified delay

});

⑧ dispatch_semaphore信号量 阻塞方式的一种,为0等待,否则减一继续

dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);//初始化为1,保证不会一开始被阻塞。

NSMutableArray *array = [NSMutableArray array];

for (int index = 0; index < 100000; index++) {

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0), ^(){

dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);//如果semaphore计数大于等于1.计数-1,返回,程序继续向下运行。如果计数为0,则等待

NSLog(@"addd :%d", index);

[array addObject:[NSNumber numberWithInt:index]];

// Increment the counting semaphore.

dispatch_semaphore_signal(semaphore);//加1});

}

NSOperation And NSOperationQueue

1、NSOperation  是一个抽象类,首先继承NSOperation  重写main函数 在main中创建一个autoreleasepool 在autoreleasepool中添加代码。

①start开始:通常不重载该方法,如果重载,必须关注像isExecuting, isFinished, isConcurrent, and isReady这些属性。

将一个operation添加到NSOperationQueue队列后,队列会自动调用该operation的start方法,执行完一些准备工作后,执行main函数。

但是,如果手动触发start方法,并没有添加到NSOperationQueue队列中,该operation将会在主线程中运行。

②dependency依赖:两个operation之间可以建立依赖关系。

NSBlockOperation *downloadOperation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"---new1-:%@",[NSThread currentThread]);

}];

NSBlockOperation *storeOperation = [NSBlockOperation blockOperationWithBlock:^{

NSLog(@"---new1-:%@",[NSThread currentThread]);

}];

[storeOperation addDependency:downloadOperation];//store依赖download 等到download操作的isFinished 为YES时,才开始执行 [storeOperation removeDependency:downloadOperation];//移除依赖关系

③priority优先级: 当你增加一个操作到队列,在调用他们的“start”之前,NSOperationQueue会查找所有的操作,优先级高的操作优先执行。同级的操作按照提交到队列的顺序执行(FIFO).

[downloadOperation setQueuePriority:NSOperationQueuePriorityHigh];//设置优先级

④completion block完成后的响应block

[blockOperation setCompletionBlock:^{

//code不一定在主线程中

}];

2、NSOperationQueue  是一个队列。一个Queue队列中可以有多个线程,不同的线程并发的执行。队列中并发操作的数目小于等于所设置的最大运行数。要定期检查(昂贵操作前后)isCancelled,确保尽快终止操作。

举个小栗子:

@implementation myOperation

//一般不需要重载 如果重载,必须关注像isExecuting, isFinished, isConcurrent, and isReady这些属性。

//- (void)start{

//

//}

- (void)main

{

@autoreleasepool {

NSLog(@"isMainThread-%d",[NSThread isMainThread]);

if (self.isCancelled)

return;

NSData *imageData = [[NSData alloc] initWithContentsOfURL:[NSURL URLWithString:@"http://images.freeimages.com/images/previews/3a2/fall-in-ontario-1056162.jpg"]];

if (self.isCancelled) {

imageData = nil;

return;

}

// do something about imageData

if (self.isCancelled)

return;

// report to somebody on main thread

}

}

使用:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];

queue.name = @"my Queue";

queue.maxConcurrentOperationCount = 3;//同时执行的最大并发数

for(int i = 0; i < 100;i++){

myOperation *operation = [[myOperation alloc] init];

[queue addOperation:operation];//不一定会立即触发

}

关于GCD和NSOperation各有所长,以具体需求取舍吧。

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

推荐阅读更多精彩内容