iOS开发之多线程详解

2015-07-07  本文已影响1240人  木_木27

多线程详解


多线程基础相关知识

进程:类似车间

系统正在运行的应用程序;每个进程都是独立的,都运行在其专用且受保护的内存空间中
每个进程可以开启多条线程

线程:类似车间里面的工人

一个iOS程序启动后会默认开启一条线程,即主线程/UI线程
是进程的执行单元,一个进程要想执行任务,只要要有一条线程;进程只会分配内存,并不会执行任务,所以执行任务必须要至少有一条线程。
例如:网易云音乐进程,那么播放音乐任务就要开启线程来执行了

一条线程中的的任务执行是串行的,也就是说在同一时间内只能执行一个任务;线程是进程中的一条执行路径

线程的状态

NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
[thread start];//线程此时具有可被调度的能力
//睡10秒
[NSThread sleepForTimeInterval:10];
//从现在起睡3秒
[NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3]];

多线程的原理

同一时间,CPU只能处理一条线程,但是如果如果CPU能快速在不同的线程之间进行调度(切换),就会造成多条线程同时执行的假象。
一个App运行之后,系统会默认开启一条线程,也就是“主线程”,也称为“UI线程”;
注意点:不要把耗时的任务放到主线程中,不然会影响UI的刷新,导致卡顿的现象,应该把耗时的任务放到子线程中


多线程的几种实现方案

技术方案 简介 语言 线程生命周期 使用频率
pthread 一套通用的多线程API;适用于Unix\Linux\Windows等系统;跨平台\可移植;使用难度大 C 程序员管理 几乎不用
NSThread 使用更加面向对象;简单易用,可以直接操作线程对象 OC 程序员管理 偶尔使用
GCD 旨在替代NSThread等线程技术;充分利用设备的多核 C 自动管理 经常使用
NSOpreation 基于GCD(底层是GCD);比GCD多了一些更简单使用的功能;使用更加面向对象 OC 自动管理 经常使用

NSThread

//第一种创建方式(显式创建并手动启动):
NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(download) object:nil];
//启动
[thread1 start];
//第二种创建方式(显式创建后自动启动,但无法对线程进行详细的设置,因为无法取得线程对象):
[NSThread detachNewThreadSelector:@selector(download:) toTarget:self withObject:@"www.baidu.com"];
//第三种创建方式(隐式创建并自动启动,但无法对线程进行详细的设置,因为无法取得线程对象):
[self performSelectorInBackground:@selector(download:) withObject:@"www.google.com.cn"];
//判断是否为主线程
+ (NSThread *)mainThread;//获得主线程
- (BOOL)isMainThread;//是否为主线程  
+ (BOOL)isMainThread;//是否为主线程 
//判断正在执行的方法是否在主线程中执行
[NSThread isMainThread];

GCD

//同步函数:不具备开启线程的能力,会阻塞当前线程,直到Block中的任务执行完毕,然后再执行当前线程的任务。
dispatch_sync(dispatch_queue_t queue, ^(void)block)
//异步函数:具备开启线程的能力,不会阻塞当前线程,即不用等待Block执行完毕才执行当前方法,当前方法执行完毕再回来执行Block里面的代码。
dispatch_async(dispatch_queue_t queue, ^(void)block)

队列:决定了任务的执行方式

//两个参数填0即可
dispatch_queue_t queue = dispatch_get_global_queue(long identifier, unsigned long flags);
DISPATCH_QUEUE_PRIORITY_HIGH 2  //高
DISPATCH_QUEUE_PRIORITY_DEFAULT 0  //默认(中)
DISPATCH_QUEUE_PRIORITY_LOW (-2)  //底
DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN  //后台

四个比较容易混淆的专业术语

同步,异步,并发,串行的组合

//全局并发队列(失去并发功能,因为并发的前提是多线程)
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//同步函数(不会开子线程)
dispatch_sync(queue, ^{
//code
});
//手动创建串行队列
dispatch_queue_t queue = dispatch_queue_create("myThread", NULL);
//同步函数(不会开子线程)
dispatch_sync(queue, ^{
//code
});
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//异步函数(一般开多条子线程来同时执行任务)
dispatch_async(queue, ^{
//code
});
//手动创建串行队列
dispatch_queue_t queue = dispatch_queue_create("myThread", NULL);
//异步函数(会开线程,但一般只开一条子线程)
dispatch_async(queue, ^{
//code
});
- (void)syncMainQueue
{
    NSLog(@"syncMainQueue----begin--");
    // 1.主队列(添加到主队列中的任务,都会自动放到主线程中去执行)
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    // 2.添加 任务 到主队列中 异步 执行
    dispatch_sync(mainQueue, ^{
        NSLog(@"-----下载图片1---%@", [NSThread currentThread]);
    });
    NSLog(@"syncMainQueue----end--");
}

队列组

//创建队列组
dispatch_group_t group = dispatch_group_create();
//全局并发队列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
//任务1
dispatch_group_async(group, queue, ^{
    //下载图片1
});
dispatch_group_async(group, queue, ^{
    //下载图片2
});

//合并图片
dispatch_group_notify(group, queue, ^{
    //合并图片
});

//回到主线程刷新界面
dispatch_async(dispatch_get_main_queue(), ^{
    //刷新
});

GCD概览

同步/异步函数 全局并发队列 手动创建串行队列 主队列
同步函数 没有开启新的线程;串行执行任务 没有开启新的线程;串行执行任务 没有开启新的线程;串行执行任务
异步函数 开启新的线程;并发执行任务 开启新的线程;串行执行任务 开启新的线程;串行执行任务

NSOperation

- (void)cancel;//取消单个操作

NSOperationQueue

- (void)addOperation:(NSOperation *)op;
- (void)addOperationWithBlock:(void (^)(void))block;
//operationB依赖操作operationA
[operationB addDependency:operationA];
//移除依赖
[operationB removeDependency:operationA];
//@property (copy) void (^completionBlock)(void)
[opetationA setCompletionBlock:^{
    //opetationB
}]
- (void)cancelAllOperations;//取消所有操作,一般用在内存警告
方法里面
@property (getter=isSuspended) BOOL suspended;//暂停(或者恢复)操作,一般在开始(完成)刷新UI界面的时候调用

单例模式

ARC环境下得单例模式:

//可以使用GCD实现,此处没采用
static id _instance;//全局变量
if (_instance == nil) { // 防止频繁加锁
    @synchronized(self) {//互斥锁
        if (_instance == nil) { // 防止创建多次
             _instance = [super allocWithZone:zone];
        }
    }
}

MRC环境下得单例模式:

- (oneway void)release{
}
- (id)autorelease{
    return self;
}
- (id)retain{
    return _instance;//return self;
}
- (NSUInteger)retainCount{
    return 1;
}

简化单例模式代码

#if __has_feature(objc_arc)
//arc环境
#else
//mrc环境
#endif

互斥锁

@synchronized(self){
    /*
    code,需要被锁的代码
    */
}

原子性和非原子性

线程间通信

//下载完成后将image传到主线程,并在downloadFinished:方法内刷新UI界面
//waitUntilDone参数:若传YES则表明:等待主线程里面的图片设置完成后再执行子线程之后的代码;传NO就不会等待主线程的操作,便直接执行子线程的代码
[self performSelectorOnMainThread:@selector(downloadFinished:) withObject:image waitUntilDone:NO];
//也可以这样优化
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:NO];

//线程间通信的常用方法一(performSelector):
[self performSelector:@selector(downloadFinished:) onThread:[NSThread mainThread] withObject:image waitUntilDone:YES];
[self.imageView performSelectorOnMainThread:@selector(setImage:) withObject:image waitUntilDone:YES];

//线程间通信的常用方法二(NSOperationQueue):
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
    // 1.异步下载图片(耗时操作)
    NSURL *url = [NSURL URLWithString:@"图片URL"];
    NSData *data = [NSData dataWithContentsOfURL:url];
    UIImage *image = [UIImage imageWithData:data];
    // 2.回到主线程,显示图片(刷新UI)
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
    }];
}];
//线程间通信的常用方法三(GCD):
dispatch_sync(dispatch_get_main_queue(), ^{
        //操作
    });
上一篇 下一篇

猜你喜欢

热点阅读