在iOS 中实现多线程的方案:
·Pthreads
·NSThread
·GCD
·NSOperation & NSOperationQueue
Pthreads
这个基于c语言的框架,所以能够跨平台,在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。
OC 代码中的使用
先得导入头文件
#import <pthread.h>
创建一个线程,并且执行任务
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
pthread_t thread;
//创建一个线程并自动执行
pthread_create(&thread, NULL, start, NULL);
}
void *start(void *data) {
NSLog(@"%@", [NSThread currentThread]); return NULL;
}
打印结果:
<NSThread: 0x6000020957c0>{number = 3, name = (null)}
pthread 是语言的函数还得手动管理生命周期,手动销毁。对于习惯了ARC的我们真的很不爽。
NSThread
这种方式是苹果封装的,完全面向对象。所以使用的时候可直接操作对象,很直观、方便。就是需要我们手动的去管理生命周期。所以 只是 [NSThread currentThread] 用来获取当前的线程类,查看当前线程的属性,调试起来还比较方便,以下下是一些用法。
创建并启动
· 先创建线程,再启动
// 创建
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];
// 启动
[thread start];
· 创建线程并启动
[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];
· 使用 NSObject 的方法创建并自动启动
[self performSelectorInBackground:@selector(run:) withObject:nil];
NSThread的一些其他方法
//取消线程
- (void)cancel;
//启动线程
- (void)start;
//判断某个线程的状态的属性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//设置和获取线程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//获取当前线程信息
+ (NSThread *)currentThread;
//获取主线程信息
+ (NSThread *)mainThread;
//使当前线程暂停一段时间,或者暂停到某个时刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
GCD
提高cpu的利用率,自动管理线程的生命周期,block 回调方式使用方便。
任务和队列
在GCD中,的任务和队列
任务:即操作,在GCD中就是一个Block,所以添加任务十分方便。任务有两种执行方式:同步执行和异步执行,他们之间的区别是 是否创建新的线程。
同步执行:只要是同步执行的任务,都会在当前线程执行,不会另开辟线程。
异步执行:只要是异步执行的任务,都会另开辟线程,在别的线程执行。
更新:
这里说的并不准确,同步(sync)和异步(async)的主要区别在于会不会阻塞主线程,直到Block中的任务执行完毕!
如果是同步(sync)操作,它会阻塞当前线程并等待Block中的任务执行完毕,然后当前线程才会继续往下运行。
如果是异步(async)操作,当前线程会直接往下执行,它不会阻塞当前线程。
·队列:用于存放任务。一共有两种队列。串行队列和并行队列。
串行队列中的任务会根据队列的定义FIFO的执行,一个接一个的先进先出的进行执行。
重新:放到串行队列的任务,GCD会FIFO(先进先出)地取出来一个,执行一个,然后取下一个,这样一个一个的执行。
并行队列中的任务 根据同步或者异步有不同的执行方式。
更新:放到并存队列的任务,GCD也会FIFO的取出来,但不同的是,他取出来一个就会放到别的线程,然后再取出一个放到另一个线程。这样由于取的动作很快忽略不计,看起来,所有的任务都是一起执行的。不过需要注意,GCD会根基系统资源控制并行的数量,所以如果任务很多,它并不会让所有任务同时执行。
总结下表
同步执行 异步执行
串行队列当前线程,一个一个执行其他线程,一个一个执行
并行队列当前线程,一个一个执行开很多线程,一起执行
创建队列
主队列:这是一个特殊的 串行队列。主队列 用来刷新UI。任何需要刷新UI的工作多需要主队列执行,所以一般耗时的任务都要放到别的线程执行。
//OBJECTIVE-C
dispatch_queue_t queue = ispatch_get_main_queue();
自己创建的队列:凡是自己创建的队列都是串行队列。
更新:自己可以创建串行队列,也可以创建 并行队列。看下面的代码(代码已更新),它有两个参数,第一个上面已经说了,第二个才是最重要的。第二个参数用来表示创建的队列是串行还是并行的。传入 DISPATCH_QUEUE_SERIAL 或NULL表示创建串行队列。传入DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。
//OBJECTIVE-C
串行队列
dispatch_queue_tqueue= dispatch_queue_create("tk.bourne.testQueue",NULL);
dispatch_queue_tqueue= dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_SERIAL);
//并行队列
dispatch_queue_tqueue= dispatch_queue_create("tk.bourne.testQueue", DISPATCH_QUEUE_CONCURRENT);
全局并行队列:这应该是唯一一个并行队列,要是并行任务一般都加入到这个队列。这是系统提供的一个并发队列。
//OBJECTIVE-C
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
创建任务
同步任务: 不会另开线程 改:会阻塞当前线程 (SYNC)
OBJECTIVE-C
dispatch_sync(<#queue#>, ^{
//code here NSLog(@"%@", [NSThread currentThread]);
});
异步任务:会另开线程 改:不会阻塞当前线程 (ASYNC)
OBJECTIVE-C
dispatch_async(<#queue#>, ^{
//code here NSLog(@"%@", [NSThread currentThread]);
});
更新
为了更好的理解同步和异步,和各种队列的使用,下面看两个示例:
示例一:
以下代码在主线程调用,结果是什么?
NSLog("之前 - %@", NSThread.currentThread())
dispatch_sync(dispatch_get_main_queue(), { () -> Void in
NSLog("sync - %@", NSThread.currentThread())
})
NSLog("之后 - %@", NSThread.currentThread())
答案:
只会打印第一句:之前 - <NSThread: 0x7fb3a9e16470>{number = 1, name = main} ,然后主线程就卡死了,你可以在界面上放一个按钮,你就会发现点不了了。
解释:
同步线程会阻塞当前的线程,然后把Block中的任务放到指定的队列中执行,只能等到Block中的任务完成后才能让当前线程继续往下运行。
那么这里的步聚就是:打印完第一步后,dispatch_sync立即阻塞当前的主线程,然后把Block中的任务放到main_queue中,可是main_queue中的任务会被取出来放到主线程中执行,但主线程这个时候已经被阻塞了,所以block中的任务就不能完成,它不完成,dispatch_sync就会一直阻塞主线程, 就不能完成,他不完成,dispatch_sync 就会一直阻塞主线程,