系统进程

AOP(切面编程)

Aspects

MultipleThread

Thread

Pthreads

POSIX线程(POSIX threads),简称Pthreads,是线程的POSIX标准。该标准定义了创建和操纵线程的一整套API。在类Unix操作系统(Unix、Linux、Mac OS X等)中,都使用Pthreads作为操作系统的线程。

- (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

这套方案是经过苹果封装后的,并且完全面向对象的。所以你可以直接操控线程对象,非常直观和方便。但是,它的生命周期还是需要我们手动管理,所以这套方案也是偶尔用用,比如 [NSThread currentThread],它可以获取当前线程类,你就可以知道当前线程的各种属性,用于调试十分方便。下面来看看它的一些用法。

  • 先创建线程类,再启动

OBJECTIVE-C

` ``// 创建`` ``NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(run:) object:nil];`` ``// 启动`` ``[thread start];`

SWIFT

` ``//创建`` ``let thread = NSThread(target: self, selector: ``"run:"``, object: nil)`` ``//启动`` ``thread.start()`
  • 创建并自动启动

OBJECTIVE-C

` ``[NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:nil];`

SWIFT

` ``NSThread.detachNewThreadSelector(``"run:"``, toTarget: self, withObject: nil)`

常见方法

//取消线程
- (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;

线程同步

线程跳转

我们都知道在其他线程操作完成后必须到主线程更新UI。所以,我们来看看有哪些方法可以回到主线程。

Asynchronous

PromiseKit

Concurrence

GCD

Grand Central Dispatch,听名字就霸气。它是苹果为多核的并行运算提出的解决方案,所以会自动合理地利用更多的CPU内核(比如双核、四核),最重要的是它会自动管理线程的生命周期(创建线程、调度任务、销毁线程),完全不需要我们管理。

blob.png

队列获取与创建

  • 主队列:特殊的串行队列

这是一个特殊的 串行队列。什么是主队列,大家都知道吧,它用于刷新 UI,任何需要刷新 UI 的工作都要在主队列执行,所以一般耗时的任务都要放到别的线程执行。

//OBJECTIVE-C
dispatch_queue_t queue = dispatch_get_main_queue();
//SWIFT
let queue = dispatch_get_main_queue()
  • 全局并行队列

这应该是唯一一个并行队列,只要是并行任务一般都加入到这个队列。

//OBJECTIVE-C
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//SWIFT
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
  • 自建队列

自己可以创建 串行队列, 也可以创建 并行队列。看下面的代码(代码已更新),它有两个参数,第一个上面已经说了,第二个才是最重要的。第二个参数用来表示创建的队列是串行的还是并行的,传入 DISPATCH_QUEUE_SERIAL 或 NULL 表示创建串行队列。传入 DISPATCH_QUEUE_CONCURRENT 表示创建并行队列。

//OBJECTIVE-C
dispatch_queue_t queue = dispatch_queue_create("tk.bourne.testQueue", NULL);
//SWIFT
let queue = dispatch_queue_create("tk.bourne.testQueue", nil);

任务

  • 同步任务:不会新开线程

//OJC
dispatch_sync(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
//Swift
dispatch_sync(, { () -> Void in
//code here
println(NSThread.currentThread())
})
  • 异步任务:会新开线程

//OJC
dispatch_sync(, ^{
//code here
NSLog(@"%@", [NSThread currentThread]);
});
//Swift
dispatch_sync(, { () -> Void in
//code here
println(NSThread.currentThread())
})

Async:GCD的语法糖

Cocoa官方提供的这一套Callback方式的GCD的写法还是有些恶心,有人对其进行了一些封装,使得GCD的使用更加的方便,可以参考Async

Async.background {
println("This is run on the background queue")
}.main {
println("This is run on the main queue, after the previous block")
}

在CocoaPods中使用:

use_frameworks!
pod "AsyncSwift"

即可以引入 (1)Supports the modern queue classes:

Async.main {}
Async.userInteractive {}
Async.userInitiated {}
Async.utility {}
Async.background {}

(2)Chain as many blocks as you want:

Async.userInitiated {
// 1
}.main {
// 2
}.background {
// 3
}.main {
// 4
}

(3)Store reference for later chaining:

let backgroundBlock = Async.background {
print("This is run on the background queue")
}
// Run other code here...
// Chain to reference
backgroundBlock.main {
print("This is run on the \(qos_class_self().description) (expected \(qos_class_main().description)), after the previous block")
}

(4)Custom queues:

let customQueue = dispatch_queue_create("CustomQueueLabel", DISPATCH_QUEUE_CONCURRENT)
let otherCustomQueue = dispatch_queue_create("OtherCustomQueueLabel", DISPATCH_QUEUE_CONCURRENT)
Async.customQueue(customQueue) {
print("Custom queue")
}.customQueue(otherCustomQueue) {
print("Other custom queue")
}

(5)Dispatch block after delay:

let seconds = 0.5
Async.main(after: seconds) {
print("Is called after 0.5 seconds")
}.background(after: 0.4) {
print("At least 0.4 seconds after previous block, and 0.9 after Async code is called")
}

(6)Cancel blocks that aren't already dispatched:

// Cancel blocks not yet dispatched
let block1 = Async.background {
// Heavy work
for i in 0...1000 {
print("A \(i)")
}
}
let block2 = block1.background {
print("B – shouldn't be reached, since cancelled")
}
Async.main {
// Cancel async to allow block1 to begin
block1.cancel() // First block is _not_ cancelled
block2.cancel() // Second block _is_ cancelled
}

(7)Wait for block to finish – an ease way to continue on current queue after background task:

let block = Async.background {
// Do stuff
}
// Do other stuff
block.wait()

AsyncGroup

Multiple dispatch blocks with GCD:
let group = AsyncGroup()
group.background {
// Run on background queue
}
group.utility {
// Run on utility queue, in parallel to the previous block
}
group.wait()
All modern queue classes:
group.main {}
group.userInteractive {}
group.userInitiated {}
group.utility {}
group.background {}
Custom queues:
let customQueue = dispatch_queue_create("Label", DISPATCH_QUEUE_CONCURRENT)
group.customQueue(customQueue) {}
Wait for group to finish:
let group = AsyncGroup()
group.background {
// Do stuff
}
group.background {
// Do other stuff in parallel
}
// Wait for both to finish
group.wait()
// Do rest of stuff
Custom asynchronous operations:
let group = AsyncGroup()
group.enter()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// Do stuff
group.leave()
}
group.enter()
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
// Do other stuff in parallel
group.leave()
}
// Wait for both to finish
group.wait()
// Do rest of stuff

队列组

队列组可以将很多队列添加到一个组里,这样做的好处是,当这个组里所有的任务都执行完了,队列组会通过一个方法通知我们。下面是使用方法,这是一个很实用的功能。

//1.创建队列组
dispatch_group_t group = dispatch_group_create();
//2.创建队列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//3.多次使用队列组的方法执行任务, 只有异步方法
//3.1.执行3次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 3; i++) {
NSLog(@"group-01 - %@", [NSThread currentThread]);
}
});
//3.2.主队列执行8次循环
dispatch_group_async(group, dispatch_get_main_queue(), ^{
for (NSInteger i = 0; i < 8; i++) {
NSLog(@"group-02 - %@", [NSThread currentThread]);
}
});
//3.3.执行5次循环
dispatch_group_async(group, queue, ^{
for (NSInteger i = 0; i < 5; i++) {
NSLog(@"group-03 - %@", [NSThread currentThread]);
}
});
//4.都完成后会自动通知
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"完成 - %@", [NSThread currentThread]);
});

NSOperation

GCD是基于c的底层api,NSOperation属于object-c类。ios 首先引入的是NSOperation,IOS4之后引入了GCD和NSOperationQueue并且其内部是用gcd实现的。相对于GCD:

1,NSOperation拥有更多的函数可用,具体查看api。

2,在NSOperationQueue中,可以建立各个NSOperation之间的依赖关系。

3,有kvo,可以监测operation是否正在执行(isExecuted)、是否结束(isFinished),是否取消(isCanceld)。

4,NSOperationQueue可以方便的管理并发、NSOperation之间的优先级。

NSOperation 只是一个抽象类,所以不能封装任务。但它有 2 个子类用于封装任务。分别是:NSInvocationOperation 和 NSBlockOperation 。创建一个Operation 后,需要调用 start 方法来启动任务,它会 默认在当前队列同步执行。当然你也可以在中途取消一个任务,只需要调用其 cancel 方法即可。不过因为NSInvocationOperation不是类型安全的,其在Swift中已经被弃用。

创建任务

//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//2.开始任务
[operation start];

之前说过这样的任务,默认会在当前线程执行。但是 NSBlockOperation 还有一个方法:addExecutionBlock: ,通过这个方法可以给 Operation 添加多个执行 Block。这样 Operation 中的任务 会并发执行,它会 在主线程和其它的多个线程 执行这些任务,注意下面的打印结果:

//1.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//2.开始任务
[operation start];

打印结果:

2015-07-28 17:50:16.585 test[17527:4095467] 第2次 -{number = 1, name = main}
2015-07-28 17:50:16.585 test[17527:4095666] 第1次 -{number = 4, name = (``null``)}
2015-07-28 17:50:16.585 test[17527:4095665]{number = 3, name = (``null``)}
2015-07-28 17:50:16.585 test[17527:4095662] 第0次 -{number = 2, name = (``null``)}
2015-07-28 17:50:16.586 test[17527:4095666] 第3次 -{number = 4, name = (``null``)}
2015-07-28 17:50:16.586 test[17527:4095467] 第4次 -{number = 1, name = main}

创建队列

看过上面的内容就知道,我们可以调用一个 NSOperation 对象的 start() 方法来启动这个任务,但是这样做他们默认是 同步执行 的。就算是 addExecutionBlock 方法,也会在 当前线程和其他线程 中执行,也就是说还是会占用当前线程。这是就要用到队列 NSOperationQueue 了。而且,按类型来说的话一共有两种类型:主队列、其他队列。只要添加到队列,会自动调用任务的 start() 方法。

  • 主线程

//OBJECTIVE-C
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//SWIFT
let queue = NSOperationQueue.mainQueue()
  • 其他队列

因为主队列比较特殊,所以会单独有一个类方法来获得主队列。那么通过初始化产生的队列就是其他队列了,因为只有这两种队列,除了主队列,其他队列就不需要名字了。注意:其他队列的任务会在其他线程并行执行。

//1.创建一个其他队列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//2.创建NSBlockOperation对象
NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"%@", [NSThread currentThread]);
}];
//3.添加多个Block
for (NSInteger i = 0; i < 5; i++) {
[operation addExecutionBlock:^{
NSLog(@"第%ld次:%@", i, [NSThread currentThread]);
}];
}
//4.队列添加任务
[queue addOperation:operation];

任务依赖

NSOperation 有一个非常实用的功能,那就是添加依赖。比如有 3 个任务:A: 从服务器上下载一张图片,B:给这张图片加个水印,C:把图片返回给服务器。这时就可以用到依赖了:

//1.任务一:下载图片
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下载图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//2.任务二:打水印
NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"打水印 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//3.任务三:上传图片
NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"上传图片 - %@", [NSThread currentThread]);
[NSThread sleepForTimeInterval:1.0];
}];
//4.设置依赖
[operation2 addDependency:operation1]; //任务二依赖任务一
[operation3 addDependency:operation2]; //任务三依赖任务二
//5.创建队列并加入任务
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperations:@[operation3, operation2, operation1] waitUntilFinished:NO];