iOS开发中的多线程

image.png

1、pthread

void * run(void *param)
{
    for (NSInteger i = 0; i<50000; i++) {
        NSLog(@"------buttonClick---%zd--%@", i, [NSThread currentThread]);
    }
    return NULL;
}

- (IBAction)buttonClick:(id)sender {
    pthread_t thread;
    pthread_create(&thread, NULL, run, NULL);
    
    pthread_t thread2;
    pthread_create(&thread2, NULL, run, NULL);
}

2、NSThread(3种创建方式)

  • 创建和启动线程


    Paste_Image.png
  • 其他创建线程的方式


    Paste_Image.png
  • 主线程相关用法


    Paste_Image.png
  • 其他用法


    Paste_Image.png
  • 控制线程的状态


    Paste_Image.png
  • 线程之间的通信
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self performSelectorInBackground:@selector(download3) withObject:nil];
}

- (void)download3
{
    // 图片的网络路径
    NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
    
    // 加载图片
    NSData *data = [NSData dataWithContentsOfURL:url];
    
    // 生成图片
    UIImage *image = [UIImage imageWithData:data];
    
    // 回到主线程,显示图片(方式一)
    [self.imageView performSelector:@selector(setImage:) onThread:[NSThread mainThread] withObject:image waitUntilDone:NO];
    //方式二
//    [self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];
    //方式三
//    [self performSelectorOnMainThread:@selector(showImage:) withObject:image waitUntilDone:YES];
}

3、GCD

将任务添加到队列交给系统去执行,任务的执行分为同步和异步,异步:具备开启线程的能力,同步:不具备开启线程的能力。队列分为串行和并行,串行,一个接一个进行,并发,可以同时进行。

  • 同步函数
//参数1:队列
 dispatch_sync(queue, ^{
        NSLog(@"100-----%@", [NSThread currentThread]);
    });
  • 异步函数
//参数1:队列
 dispatch_async(queue, ^{
        NSLog(@"100-----%@", [NSThread currentThread]);
    });
  • 串行队列
// 参数1:队列名称;参数2:队列的类型(SERIAL:串行,CONCURRENT:并发,NULL:默认串行)
dispatch_queue_t queue = dispatch_queue_create("串行队列1", DISPATCH_QUEUE_SERIAL);

获取主队列(默认就是串行):

// 1.获得主队列(不管是同步函数还是异步函数创建的任务,只要添加到主队列都只会在主线程中执行,不会开启新的线程)
dispatch_queue_t queue = dispatch_get_main_queue();
  • 并发队列
// 参数1:队列名称;参数2:队列的类型(SERIAL:串行,CONCURRENT:并发,NULL:默认串行)
dispatch_queue_t queue = dispatch_queue_create("并发队列1", DISPATCH_QUEUE_CONCURRENT);

获取全局队列(默认就是并发):

//全局并发队列的优先级
//#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 // 后台
//参数1:队列优先级(一般使用default);参数2:暂时无用,用0即可
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
Paste_Image.png
  • barrier屏障

前面的任务执行结束之后barrier才执行,barrier执行完之后它后面的任务才能执行。注意:queue不能为全局并发队列

dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
  • 延时执行
//2秒后打印run
 dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            NSLog(@"run-----");
        });
  • 一次性代码

使用场景:单例

    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"------run");
    });
  • 快速遍历(迭代)

开启多个线程同时进行

 dispatch_apply(10, queue, ^(size_t index) {
        NSLog(@"%@---%zd", [NSThread currentThread], index);
    });
  • 队列组

保证组里面的任务都执行完之后再往下执行。例如:线程1和线程2分别从网上下载1张图片,线程3需要对这两张图片进行合成。(也可以使用barrier函数)

//全局队列
 dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
  // 1、创建一个队列组
    #dispatch_group_t group = dispatch_group_create();
  // 2.下载图片操作添加到组
  //下载图片1
    #dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        self.image1 = [UIImage imageWithData:data];
    });
    
    //下载图片2
    #dispatch_group_async(group, queue, ^{
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        self.image2 = [UIImage imageWithData:data];
    });
    
    // 3.将图片1、图片2合成一张新的图片
    #dispatch_group_notify(group, queue, ^{
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 绘制图片
        [self.image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        [self.image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        
        // 取得上下文中的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 结束上下文
        UIGraphicsEndImageContext();
        
        // 回到主线程显示图片
        dispatch_async(dispatch_get_main_queue(), ^{
            // 4.将新图片显示出来 
            self.imageView.image = image;
        });
    });

等待循环里多个请求都请求完成:
group.png

4、NSOperation

NSOperation是个抽象类,并不具备封装操作的能力,必须使用它的子类(三种方式:


Paste_Image.png

)

  • NSInvocationOperation(配合队列一起使用才有意义)
- (void)invocationOperation
{
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
    //默认情况下,调用了start方法后并不会开一条新线程去执行操作,而是在当前线程同步执行操作
    //只有将NSOperation放到一个NSOperationQueue中,才会异步执行操作
    [op start];
}

- (void)run
{
    NSLog(@"------%@", [NSThread currentThread]);
}
  • NSBlockOperation
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    [self blockOperation];
}

- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主线程
        NSLog(@"下载1------%@", [NSThread currentThread]);
    }];
    
    // 添加额外的任务(在子线程执行)
    [op addExecutionBlock:^{
        NSLog(@"下载2------%@", [NSThread currentThread]);
    }];

    [op addExecutionBlock:^{
        NSLog(@"下载3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下载4------%@", [NSThread currentThread]);
    }];
    
    [op start];
}
  • 任务添加到队列
- (void)operationQueue1
{
    // 创建队列(同时具备串行和并发的功能,默认创建方式是并发)
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 创建操作(任务)
    // 创建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download2) object:nil];
    
    // 创建NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    [op3 addExecutionBlock:^{
        NSLog(@"download5 --- %@", [NSThread currentThread]);
    }];
    
    
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download6 --- %@", [NSThread currentThread]);
    }];
    
    // 创建XMGOperation
    XMGOperation *op5 = [[XMGOperation alloc] init];
    
    // 添加任务到队列中,只要添加到队列中,系统会自动帮我们调用任务的start
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op2]; // [op2 start]
    [queue addOperation:op3]; // [op3 start]
    [queue addOperation:op4]; // [op4 start]
    [queue addOperation:op5]; // [op5 start](自定义的队列需要在.m文件里重写main函数)

//也可以不用手动添加任务,直接添加一个block,内部会自动创建并添加
 [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}
#import "XMGOperation.h"

@implementation XMGOperation

/**
 * 需要执行的任务
 */
- (void)main
{
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download1 -%zd-- %@", i, [NSThread currentThread]);
    }
//建议执行一段比较耗时的操作后,添加取消判断。否则,外面在调用取消操作的时候,并不能取消这里的任务)
    if (self.isCancelled) return;
    
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download2 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
    
    for (NSInteger i = 0; i<1000; i++) {
        NSLog(@"download3 -%zd-- %@", i, [NSThread currentThread]);
    }
    if (self.isCancelled) return;
}

@end
  • 其他方法
//设置最大并发数
    - (NSInteger)maxConcurrentOperationCount;
    - (void)setMaxConcurrentOperationCount:(NSInteger)cnt;
//取消队列的所有操作
    - (void)cancelAllOperations;
    //提示:也可以调用NSOperation的- (void)cancel方法取消单个操作
    
    //暂停和恢复队列
    - (void)setSuspended:(BOOL)b; // YES代表暂停队列,NO代表恢复队列
    - (BOOL)isSuspended;

   //NSOperation之间可以设置依赖来保证执行顺序
    //比如一定要让操作A执行完后,才能执行操作B,可以这么写
    [operationB addDependency:operationA]; // 操作B依赖于操作A

//可以监听一个操作的执行完毕(监听完成之后所执行的任务不一定和之前操作执行的线程相同,但是是在子线程)
    - (void (^)(void))completionBlock;
    - (void)setCompletionBlock:(void (^)(void))block;
  • 线程之间的通信

下载图片

- (void)test
{
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        // 图片的网络路径
       NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        // 生成图片
        UIImage *image = [UIImage imageWithData:data];
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
}

合成图片

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //防止block操作还没完成image1这个局部变量就销毁了
    __block UIImage *image1 = nil;
    // 下载图片1
    NSBlockOperation *download1 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        image1 = [UIImage imageWithData:data];
    }];
    
    __block UIImage *image2 = nil;
    // 下载图片2
    NSBlockOperation *download2 = [NSBlockOperation blockOperationWithBlock:^{
        
        // 图片的网络路径
        NSURL *url = [NSURL URLWithString:@"http://pic38.nipic.com/20140228/5571398_215900721128_2.jpg"];

        
        // 加载图片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成图片
        image2 = [UIImage imageWithData:data];
    }];
    
    // 合成图片
    NSBlockOperation *combine = [NSBlockOperation blockOperationWithBlock:^{
        // 开启新的图形上下文
        UIGraphicsBeginImageContext(CGSizeMake(100, 100));
        
        // 绘制图片
        [image1 drawInRect:CGRectMake(0, 0, 50, 100)];
        image1 = nil;
        
        [image2 drawInRect:CGRectMake(50, 0, 50, 100)];
        image2 = nil;
        
        // 取得上下文中的图片
        UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
        
        // 结束上下文
        UIGraphicsEndImageContext();
        
        // 回到主线程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
    //添加依赖保证图片先下载完成
    [combine addDependency:download1];
    [combine addDependency:download2];
    
    [queue addOperation:download1];
    [queue addOperation:download2];
    [queue addOperation:combine];
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容