iOS多线程编程

一、多线程概述

  • 一个程序进入内存运行时就成了一个进程,一个进程包含若干线程,其中必有一个主线程.线程之间运行是独立的.

  • 多线程优势

  1. 线程之间容易共享内存,进程间不能.
  2. 充分发挥多核处理器的优势,不同任务分配给不同处理器,进入“并行运算”的状态
  3. 将耗时、轮询或并发需求高的任务分配到其他线程进行,主线程负责统一更新界面,这样使用户体验更好.
  • 多线程劣势
  1. 占内存(默认住线程1M 子线程512K)
  2. CPU开销大(一般同时开启线程小于5个).
  3. 程序设计变得复杂.
  • 多线程状态
    1.新建(New):分配内存,初始化内部成员变量的值
    2.就绪(Runable):创建方法调用的栈和计数器
    3.运行(Running):运行状态
    4.终止(Exit):线程终止
    5.阻塞(Blocked):线程需要暂停一段时间

二、iOS多线程技术

  • pthread
    1.跨平台,可移植
    2.使用难度大
    3.C语言

  • NSThread
    1.面向对象
    2.直接操作线程对象
    3.Objective-C

  • GCD
    1.替代NSThread
    2.充分利用多核技术
    3.C语言

  • NSOperation
    1.基于GCD,更简单
    2.更面向对象
    3.Objective-C

三、NSThread三种创建线程的方法及阻塞线程的测试

在storyboard里放一个Button和一个Text View(用来测试线程的阻塞)

  • 线程一启动就会执行selfrun:方法
  • 测试阻塞线程时,由于test方法处在主线程,Debug栏一直输出,所以拖动文本视图没有任何反应.
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}

//获取当前、主线程
- (IBAction)ClickBtn:(id)sender {
    NSThread *current = [NSThread currentThread];
    NSLog(@"Current thread %@ ",current);
    
    NSThread *main = [NSThread mainThread];
    NSLog(@"Main thread %@",main);    

    [self threadCreate1];

    //第二种方法  [self threadCreate2];
    //第三种方法  [self threadCreate3];
    //测试阻塞方法  [self threadTest];
}

//输出当前线程
- (void)run:(NSString *)param{
    NSThread *current = [NSThread currentThread];
    for(int i = 0; i<10; i++){
        NSLog(@"%@ run %@",current,param);
    }
}

- (void)threadCreate1{
    NSThread *threadA =[[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"线程A参数"];
    threadA.name = @"线程A";
    [threadA start];
    
    NSThread *threadB =[[NSThread alloc]initWithTarget:self selector:@selector(run:) object:@"线程B参数"];
    threadB.name = @"线程B";
    [threadB start];
}

- (void)threadCreate2{
    [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"方式2参数"];
}

//隐式创建并启动线程,在后台线程中执行
- (void)threadCreate3{
    [self performSelectorInBackground:@selector(run:) withObject:@"方式3参数"];
}

//测试阻塞线程
- (void)threadTest{
    NSLog(@"%@",[NSThread currentThread]);
    for (int i=0 ; i<10000; i++) {
        NSLog(@"-----%d",i);
    }
}              
@end

四、线程间的安全隐患

  • 线程共享会造成资源抢夺,引起数据错乱及安全问题
加锁前后示意图
  • 使用 @synchronized(obj) 修饰同步代码块

五、线程间的通信

  • 主线程的方法 performSelectorOnMainThread:(SEL) withObject:(id) waitUntilDone:(BOOL)
  • 子线程的方法 performSelector:(SEL) onThread:(NSThread *) withObject:(id) waitUntilDone:(BOOL)

GCD(Grand Central Dispatch)

  • 基于C语言
  • 系统完全管理线程, 无需编写线程代码

一、队列(Dispatch Queue)

  • 将长期运行任务(代码块)拆成多个工作单元并添加到队列中,系统代为管理并放到多个线程上执行
  • FIFO(先进先出)原则,先进队列先处理,但由于执行时间不同,不一定先结束
  • 分类:
    1.Serial Dispatch Queue(串行队列): 底层线程池只有一个线程,一次执行一个任务,执行完一个任务才能执行下一个
    2.Concurrent Dispatch Queue(并行队列): 底层线程池有多个线程,按FIFO原则执行多任务

二、创建队列

1.获取全局并发队列(Global Concurrent Dispatch Queue)
  • 全局并发队列可以按FIFO顺序并行执行多个任务
  • dispatch_get_global_queue(long identifier, unsigned long flags)获取队列
    第一个参数用于指定队列优先级,含4个宏定义常量:
    #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 //后台
2.创建串、并行队列(Serial And Concurrent Dispatch Queue)
  • 创建队列
    dispatch_queue_t
    dispatch_queue_create(const char * label, dispatch_queue_attr_t attr)
    第一个参数表示队列的字符串
    第二个参数控制串并发队列(设为NULL默认串行):
    DISPATCH_QUEUE_SERIAL //串行
    DISPATCH_QUEUE_CONCURRENT //并行
3.获取主队列
  • 主队列在主线程中执行
  • dispatch_get_main_queue()获取主队列

三、提交任务

1.同步添加任务到队列,不具备开启新线程能力,阻塞调用线程直到相应任务完成
  • 调用函数
    dispatch_sync(dispatch_queue_t queue, ^(void)block) //代码块
    dispatch_sync_f(dispatch_queue_t queue, void *context, dispatch_function_t work) //函数
2.异步在新线程执行任务,可以让线程池立即执行,调用其他线程做其他事情,应尽可能使用异步.
  • 调用函数:
    dispatch_async(dispatch_queue_t queue, ^(void)block) //代码块
    dispatch_async_f(dispatch_queue_t queue, void *context, dispatch_function_t work) //函数
  • 在storyboard放两个Button分别执行串、并行任务
#import "ViewController.h"

@interface ViewController ()

@end

@implementation ViewController

dispatch_queue_t serialQueue;
dispatch_queue_t globalQueue;

- (void)viewDidLoad {
    [super viewDidLoad];
    //创建串行队列
    serialQueue = dispatch_queue_create("serQ", DISPATCH_QUEUE_SERIAL);
    //全局并发队列
    globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}

//串行异步执行任务
- (IBAction)asynSerial:(id)sender {
    dispatch_async(serialQueue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"%@  task1  %d",[NSThread currentThread],i);
        }
    });
    dispatch_async(serialQueue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"%@  task2  %d",[NSThread currentThread],i);
        }
    });
}

//并行异步执行任务
- (IBAction)asynConcurrent:(id)sender {
    dispatch_async(globalQueue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"%@  task1  %d",[NSThread currentThread],i);
        }
    });
    
    dispatch_async(globalQueue, ^{
        for (int i = 0; i<10; i++) {
            NSLog(@"%@  task2  %d",[NSThread currentThread],i);
        }
    });
}
@end

四、单次或重复执行任务

  • 单次执行dispatch_once(dispatch_once_t * predicate, ^(void)block)
  • 多次执行dispatch_apply(size_t iterations, dispatch_queue_t queue, ^(size_t)block)

五、调度队列组

  • 将多个block组成一组,监听此组任务是否全部完成.
    1.创建队列组对象dispatch_group_create()
    2.调度队列组dispatch_group_async()
    3.通知:任务执行完后通知执行其他操作dispatch_group_notify()

NSOperation

  • NSOperation实例代表一个多线程任务

新建一个继承NSOperation的类.

//Download.h

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>

@class Download;

//定义代理
@protocol DownloadDelegate <NSObject>

//下载方法
- (void)download:(Download *)operation image:(UIImage *)image;

@end

@interface Download : NSOperation

@property (nonatomic, strong)NSString *url;

//代理属性
@property (nonatomic, weak)id<DownloadDelegate>delegate;
@end
//Download.m

#import "Download.h"

@implementation Download

//重写main方法
- (void)main
{
    @autoreleasepool {
        NSURL *url = [NSURL URLWithString:self.url];
        NSData *data = [NSData dataWithContentsOfURL:url];
        UIImage *image = [UIImage  imageWithData:data];
        [[NSOperationQueue mainQueue]addOperationWithBlock:^{
            NSLog(@"download finished");
            if ([self.delegate respondsToSelector:@selector(download:image:)]) {
                [self.delegate download:self image:image];
            }
        }];
    }
}
@end

在storyboard中放一个Image View

//ViewController.m

#import "ViewController.h"
#import "Download.h"

@interface ViewController ()<DownloadDelegate>

@property (strong, nonatomic) IBOutlet UIImageView *imageView;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    //创建队列
    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
    
    //下载操作
    Download *operation = [[Download alloc]init];
    operation.delegate = self;
    operation.url =@"https://mir-s3-cdn-cf.behance.net/project_modules/max_1200/943ff442182699.57c4298d2eb2a.png" ;

    //操作加到队列中
    [queue addOperation:operation];
}

//执行操作
- (void)download:(Download *)operation image:(UIImage *)image{
    self.imageView.image = image;
}

- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
@end
运行结果

若提示:
App Transport security has blocked a cleartext HTTP (http://) resource load since it is insecure. Temporary exceptions can be configured via your app's Info.plist file.
解决方法:


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

推荐阅读更多精彩内容

  • 1. GCD简介 iOS开发中多线程的API主要有pthread,NSThread,NSOperation和GCD...
    安东_Ace阅读 1,246评论 0 6
  • 一、简介 在iOS所有实现多线程的方案中,GCD应该是最有魅力的,因为GCD本身是苹果公司为多核的并行运算提出的解...
    Alanxx阅读 349评论 0 1
  • 本文的写作目的是为学习记录,同时分享给大家,希望大神能够对文中错误的理解进行指正。 如果文章内容涉及到其他已经发表...
    油菜小白阅读 673评论 0 2
  • 背景 担心了两周的我终于轮到去医院做胃镜检查了!去的时候我都想好了最坏的可能(胃癌),之前在网上查的症状都很相似。...
    Dely阅读 9,226评论 21 42
  • 随着燕姐的不告而别,四月就这么结束了 四月是你的谎言 留给我希望 又让我绝望 01 当我竭尽全力妄图用繁杂的工作...
    铱璱阅读 232评论 0 2