系统进程

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];
上一页