[Cocoa]_[总结异步调用函数的方法]

说起异步,其实也是用多线程实现的,所以先讲解下多线程的原理,多线程就是给各个线程分时间片,但各个时间片时间又很短,所以看起来像是并发执行。多线程的好处是可以避免阻塞造成的CPU计算时间浪费,可以显著提高CPU的利用率。

异步调用函数就是一个可以无需等待被调用函数的返回值就让操作继续进行的方法,其原理其实就是把这些复制的运算添加到一个新的线程里面去并利用回调函数通知操作完成,在objective-c还可以用block来替代回调函数。一般是需要处理一些复制的业务逻辑,需要较长时间去处理,这时候是不能让它阻塞主线程,导致界面未响应,所以需要异步调用这些复杂的操作。

下面会举例说明,例子代码都自定义类Async中实现,再通过main.m中用单元测试逐个测试。

1.performSelector

NSObject提供了以performSelector为前缀的一系列方法。它们可以让用户在指定线程中,或者立即,或者延迟执行某个方法调用。这个方法给了用户实现多线程编程最简单的方法。 我们这里只讨论异步调用的方法。

在后台线程中执行方法,也就是异步调用的方法:

例子是在控制台中执行,所以需要自定义一个类来演示,自定义类如下

Async.h

#import <Foundation/Foundation.h>

@interface Async : NSObject
{
    BOOL gFinish;
}

- (void)TestAsyncPerform;
- (void)TestMyOperation;
- (void)TestNSInvocationOperation;
- (void)TestNSBlockOperation;
- (void)TestNSOperationQueue;
- (void)TestGCD1;
- (void)TestGCD2;
- (void)TestGCD3;
- (void)TestGCD4;

@end

Async.m中实现如下方法:

- (void)TestAsyncPerform
{
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    //[self performSelectorInBackground:@selector(run:) withObject:@"performSelector"];
    [self performSelectorOnMainThread:@selector(run:) withObject:@"performSelector" waitUntilDone:NO];
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}

-(void)run:(NSString *)str
{
    if ([NSThread isMainThread]) {
        NSLog(@"%@:当前在主线程中工作",str);
    }
    else
    {
        NSLog(@"%@:当前在工作线程中工作",str);
    }
    sleep(1);
    gFinish = YES;
}

mian.mm用单元测试调用:

TEST(Async, perform)
{
    Async *async = [[Async alloc] init];
    [async TestAsyncPerform];
}

int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}

输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.perform

2015-10-20 15:33:50.779 test_asynchronous[1568:303] 当前在主线程中工作

2015-10-20 15:33:50.782 test_asynchronous[1568:2003] perform:当前在工作线程中工作

[       OK ] Async.perform (4004 ms)

[----------] 1 test from Async (4004 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (4004 ms total)

[  PASSED  ] 1 test.

该方法的操作和主线程不同,说明是异步的。


2.NSOperation

(1)NSOperation是一个用来封装代码和数据交互的单任务的抽象类,因为是抽象类,所以 不能直接使用,但可以通过继承产生子类或者用官方定义的两个子类:
(2)NSInvocationOperation和NSBlockOperation,一个是把操作放进一个方法中执行,一个是把操作放进一个块中执行。
(3)一个Operation对象是一个单发对象,一旦执行了就不能被再执行以此。一般情况下都是把Operation添加到Operation queue中来执行,Operation queue不是单线程执行的,而是发里面的Operation在一个新的线程中执行,或者直接使用GCD,建议直接使用队列。
(4)如果你不想通过Operation queue来执行Operation里面的操作,可以直接使用Operation的start方法来执行。 可以通过Dependencies来让各个Operation按你想要的顺序执行,直接使用addDependency和removeDependency来添加或删除Dependencies.

(5)NSOperation 提供以下任务优先级,以这些优先级设置变量 queuePriority 即可加快或者推迟操作的执行:

NSOperationQueuePriority VeryHigh

NSOperationQueuePriority High

NSOperationQueuePriority Normal

NSOperationQueuePriority Low

NSOperationQueuePriority VeryLow

(6)NSOperation使用状态机模型来表示状态。通常,你可以使用KVO(Key-Value Observing)观察任务的执行状态。这是其他多线程工具所不具备的功能。NSOperation提供以下状态:

isReady

isExecuting

isFinished 

(7)NSOperation 对象之间的依赖性可以用如下代码表示:

[refreshUIOperation addDependency:requestDataOperation];
[operationQueue addOperation:requestDataOperation];
[operationQueue addOperation:refreshUIOperation];

除非 requestDataOperation的状态isFinished返回YES,不然refreshUIOperation这个操作不会开始。 

(8)NSOperation 还有一个非常有用功能,就是取消。这是其他多线程工具(包括后面要讲到的GCD)都没有的。调用NSOperationcancel:方法即可取消该任务。当你知道这个任务没有必要再执行下去时,尽早安全地取消它将有利于节省系统资源。 

(9)若是打算子类化Operation而不是添加到队列中执行,可以设计成同步的或者异步的。默认情况下是同步的,将操作添加到main内,直接使用start方法会阻塞当前线程直到里面的操作完成。若想异步执行里面的操作,其实就是重写start方法让其产生一个新的线程来执行所需的操作,或者用block和GCD,而且要还要使用KVC和KVO,需要较多的工作来实现,而且较为复杂。通常我们都是直接添加到队列中,因为在队列中会自动产生一个新的线程来执行Operation的操作,不管这个Operation本身被设计成同步还是异步的。


举例NSOperation的三种使用并验证观察任务状态。

(1).自定义的Operation类,把操作写在main函数里面:

MyOperation.h

#import <Foundation/Foundation.h>

@interface MyOperation : NSOperation

@end

MyOperation.m

#import "MyOperation.h"

@implementation MyOperation

-(void)main
{
    if ([NSThread isMainThread]) {
        NSLog(@"MyOperation:当前在主线程中工作");
    }
    else
    {
        NSLog(@"MyOperation:当前在工作线程中工作");
    }
    sleep(1);
}

@end

添加一个观察方法去观察任务的状态:

-(void)ObserveState1:(NSOperation *)operation
{
    while (true)
    {
        //sleep(1);
        if ([operation isExecuting]) {
            NSLog(@"MyOperation 任务执行中");
        }
        else if ([operation isFinished])
        {
            NSLog(@"MyOperation 任务完成");
            gFinish = YES;
            break;
        }
        else if ([operation isReady])
        {
            NSLog(@"MyOperation 任务准备中");
        }
    }
}

同样在Async.m中实现
- (void)TestMyOperation
{
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    MyOperation *operation1 = [[MyOperation alloc] init];
    //观察任务状态
    [self performSelectorInBackground:@selector(ObserveState1:) withObject:operation1];
    [operation1 start];
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
    return;
}
在main.mm中调用
TEST(Async, MyOperation)
{
     Async *async = [[Async alloc] init];
     [async TestMyOperation];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}
输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.MyOperation

2015-10-21 11:34:15.893 test_asynchronous[837:303] 当前在主线程中工作

2015-10-21 11:34:15.896 test_asynchronous[837:2103] MyOperation任务准备中

2015-10-21 11:34:15.897 test_asynchronous[837:2103] MyOperation任务执行中

2015-10-21 11:34:15.898 test_asynchronous[837:2103] MyOperation任务执行中

.....

2015-10-21 11:34:16.897 test_asynchronous[837:303] MyOperation:当前在主线程中工作

2015-10-21 11:34:16.898 test_asynchronous[837:2103] MyOperation任务执行中

2015-10-21 11:34:16.899 test_asynchronous[837:2103] MyOperation任务完成

[       OK ] Async.MyOperation (5005 ms)

[----------] 1 test from Async (5005 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (5006 ms total)

[  PASSED  ] 1 test.


用NSOperationQueue的好处是可以一次添加多个任务,并且NSOperation的一大特点是可以查看任务的状态并且可以中途停止。


(2) NSInvocationOperation
添加观察任务状态方法:
-(void)ObserveState2:(NSOperation *)operation
{
    while (true)
    {
        //sleep(1);
        if ([operation isExecuting]) {
            NSLog(@"NSInvocationOperation 任务执行中");
        }
        else if ([operation isFinished])
        {
            NSLog(@"NSInvocationOperation 任务完成");
            gFinish = YES;
            break;
        }
        else if ([operation isReady])
        {
            NSLog(@"NSInvocationOperation 认准准备中");
        }
    }
}
在Async.m的实现
- (void)TestNSInvocationOperation
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self
                                                                            selector:@selector(run:)
                                                                              object:@"NSInvocationOperation"];
    [self performSelectorInBackground:@selector(ObserveState2:) withObject:operation2];
    [operation2 start];
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}
main.mm的调用
TEST(Async, NSInvocationOperation)
{
    Async *async = [[Async alloc] init];
    [async TestNSInvocationOperation];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}

输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.NSInvocationOperation

2015-10-21 11:39:29.341 test_asynchronous[854:303] 当前在主线程中工作

2015-10-21 11:39:29.344 test_asynchronous[854:2003] NSInvocationOperation任务执行中

2015-10-21 11:39:29.344 test_asynchronous[854:303] NSInvocationOperation:当前在主线程中工作

2015-10-21 11:39:29.345 test_asynchronous[854:2003] NSInvocationOperation任务执行中

2015-10-21 11:39:29.345 test_asynchronous[854:2003] NSInvocationOperation任务执行中

.....

2015-10-21 11:39:30.360 test_asynchronous[854:2003] NSInvocationOperation任务完成

[       OK ] Async.NSInvocationOperation (5006 ms)

[----------] 1 test from Async (5006 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (5006 ms total)

[  PASSED  ] 1 test.

(3)NSBlockOperation
添加观察任务状态方法:
-(void)ObserveState3:(NSOperation *)operation
{
    while (true)
    {
        //sleep(1);
        if ([operation isExecuting]) {
            NSLog(@"NSBlockOperation 任务执行中");
        }
        else if ([operation isFinished])
        {
            NSLog(@"NSBlockOperation 任务完成");
            gFinish = YES;
            break;
        }
        else if ([operation isReady])
        {
            NSLog(@"NSBlockOperation 任务准备中");
        }
    }
}
在Async.m的实现
- (void)TestNSBlockOperation
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
    sleep(1);
    if ([NSThread isMainThread]) {
        NSLog(@"NSBlockOperation:当前在主线程中工作");
    }
    else
    {
        NSLog(@"NSBlockOperation:当前在工作线程中工作");
    }
    }];
    [self performSelectorInBackground:@selector(ObserveState3:) withObject:operation3];
    [operation3 start];
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}

在main.mm

TEST(Async, NSBlockOperation)
{
    Async *async = [[Async alloc] init];
    [async TestNSBlockOperation];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}

输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.NSBlockOperation

2015-10-21 13:36:40.518 test_asynchronous[1031:303] 当前在主线程中工作

2015-10-21 13:36:40.521 test_asynchronous[1031:2003] NSBlockOperation任务执行中

2015-10-21 13:36:40.521 test_asynchronous[1031:2003] NSBlockOperation任务执行中

2015-10-21 13:36:40.522 test_asynchronous[1031:2003] NSBlockOperation任务执行中

.....

2015-10-21 13:36:41.521 test_asynchronous[1031:303] NSBlockOperation:当前在主线程中工作

2015-10-21 13:36:41.521 test_asynchronous[1031:2003] NSBlockOperation任务执行中

2015-10-21 13:36:41.521 test_asynchronous[1031:2003] NSBlockOperation任务完成

[       OK ] Async.NSBlockOperation (5004 ms)

[----------] 1 test from Async (5004 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (5004 ms total)

[  PASSED  ] 1 test.


(4)NSOperationQueue

在Async.m中的实现

- (void)TestNSOperationQueue
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //2.operation
    //(1)MyOperation
    MyOperation *operation1 = [[MyOperation alloc] init];
    [self performSelectorInBackground:@selector(ObserveState1:) withObject:operation1];
    [operation1 setQueuePriority:NSOperationQueuePriorityVeryLow];

    //(2)NSInvocationOperation
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self
                                                                            selector:@selector(run:)
                                                                              object:@"NSInvocationOperation"];
    [self performSelectorInBackground:@selector(ObserveState2:) withObject:operation2];
    [operation2 setQueuePriority:NSOperationQueuePriorityNormal];

    //(3)NSBlockOperation
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        if ([NSThread isMainThread]) {
            NSLog(@"NSBlockOperation:当前在主线程中工作");
        }
        else
        {
            NSLog(@"NSBlockOperation:当前在工作线程中工作");
        }
        sleep(1);
    }];
    [self performSelectorInBackground:@selector(ObserveState3:) withObject:operation3];
    [operation3 setQueuePriority:NSOperationQueuePriorityVeryHigh];
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}

在main.mm中调用

TEST(Async, NSOperationQueue)
{
    Async *async = [[Async alloc] init];
    [async TestNSOperationQueue];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}

输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.NSOperationQueue

2015-10-21 13:39:57.854 test_asynchronous[1047:303] 当前在主线程中工作

2015-10-21 13:39:57.856 test_asynchronous[1047:2003] MyOperation任务准备中

2015-10-21 13:39:57.857 test_asynchronous[1047:2a03] NSInvocationOperation认准准备中

2015-10-21 13:39:57.857 test_asynchronous[1047:3003] NSBlockOperation任务准备中

2015-10-21 13:39:57.857 test_asynchronous[1047:2003] MyOperation任务准备中

2015-10-21 13:39:57.857 test_asynchronous[1047:2a03] NSInvocationOperation认准准备中

2015-10-21 13:39:57.857 test_asynchronous[1047:3107] NSBlockOperation:当前在工作线程中工作

2015-10-21 13:39:57.858 test_asynchronous[1047:3003] NSBlockOperation任务执行中

2015-10-21 13:39:57.859 test_asynchronous[1047:2a03] NSInvocationOperation任务执行中

2015-10-21 13:39:57.859 test_asynchronous[1047:2003] MyOperation任务执行中

.....

2015-10-21 13:39:58.859 test_asynchronous[1047:2003] MyOperation任务完成

2015-10-21 13:39:58.860 test_asynchronous[1047:2a03] NSInvocationOperation任务执行中

2015-10-21 13:39:58.861 test_asynchronous[1047:2a03] NSInvocationOperation任务完成

2015-10-21 13:39:58.861 test_asynchronous[1047:3003] NSBlockOperation任务完成

[       OK ] Async.NSOperationQueue (4004 ms)

[----------] 1 test from Async (4004 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (4004 ms total)

[  PASSED  ] 1 test.

为了方便查看,在观察方法中等待一秒,如下:

-(void)ObserveState1:(NSOperation *)operation
{
    while (true)
    {
        sleep(1);
        if ([operation isExecuting]) {
            NSLog(@"MyOperation 任务执行中");
        }
        else if ([operation isFinished])
        {
            NSLog(@"MyOperation 任务完成");
            gFinish = YES;
            break;
        }
        else if ([operation isReady])
        {
            NSLog(@"MyOperation 任务准备中");
        }
    }
}
另外两个也一样。

检验依赖关系:

- (void)TestNSOperationQueue
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];

    //2.operation
    //(1)MyOperation
    MyOperation *operation1 = [[MyOperation alloc] init];
    [self performSelectorInBackground:@selector(ObserveState1:) withObject:operation1];
    [operation1 setQueuePriority:NSOperationQueuePriorityVeryLow];

    //(2)NSInvocationOperation
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self
                                                                            selector:@selector(run:)
                                                                              object:@"NSInvocationOperation"];
    [self performSelectorInBackground:@selector(ObserveState2:) withObject:operation2];
    [operation2 setQueuePriority:NSOperationQueuePriorityNormal];

    //(3)NSBlockOperation
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        if ([NSThread isMainThread]) {
            NSLog(@"NSBlockOperation:当前在主线程中工作");
        }
        else
        {
            NSLog(@"NSBlockOperation:当前在工作线程中工作");
        }
        sleep(1);
    }];
    [self performSelectorInBackground:@selector(ObserveState3:) withObject:operation3];
    [operation3 setQueuePriority:NSOperationQueuePriorityVeryHigh];
    
    [operation3 addDependency:operation1];
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}

输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.NSOperationQueue

2015-10-21 14:10:34.714 test_asynchronous[1102:303] 当前在主线程中工作

2015-10-21 14:10:34.717 test_asynchronous[1102:1403] NSInvocationOperation:当前在工作线程中工作

2015-10-21 14:10:35.718 test_asynchronous[1102:2003] MyOperation任务执行中

2015-10-21 14:10:35.718 test_asynchronous[1102:1a03] MyOperation:当前在工作线程中工作

2015-10-21 14:10:35.718 test_asynchronous[1102:2a03] NSInvocationOperation任务执行中

2015-10-21 14:10:35.720 test_asynchronous[1102:1a03] NSBlockOperation:当前在工作线程中工作

2015-10-21 14:10:36.718 test_asynchronous[1102:3003] NSBlockOperation任务执行中

2015-10-21 14:10:36.721 test_asynchronous[1102:2003] MyOperation任务完成

2015-10-21 14:10:36.721 test_asynchronous[1102:2a03] NSInvocationOperation任务完成

2015-10-21 14:10:37.720 test_asynchronous[1102:3003] NSBlockOperation任务完成

[       OK ] Async.NSOperationQueue (4004 ms)

[----------] 1 test from Async (4004 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (4004 ms total)

[  PASSED  ] 1 test.


第三个依赖第一个,尽管第三个优先级最高,但是还是会等待第一个完成后再执行。

3.GCD

 GCD(Grand Central Dispatch)是Apple公司为了提高OS XiOS系统在多核处理器上运行并行代码的能力而开发的一系列相关技术,它提供了对线程的高级抽象。GCD是一整套技术,包含了语言级别的新功能,运行时库,系统级别的优化,这些一起为并发代码的执行提供了系统级别的广泛优化。所以,GCD也是Apple推荐的多线程编程工具。 

GCD 提供了一套纯 C API。但是,它提供的API简单易用并且有功能强大的任务管理和多线程编程能力。GCD需要和blocks(Objective-C的闭包)配合使用。blockGCD执行单元。GCD的任务需要被拆解到block中。block被排入GCD的分发队列,GCD会为你排期运行。GCD创建, 

所有的dispatch objective都是objective-c对象,这意味着当ARC开启了的时候是不需要主动调用dispatch_retain或dispatch_release 来释放资源,若不启用ARC就要主动调用释放。若你想在ARC开启了的情况下调用dispatch_retain或dispatch_release 来释放资源,可以通过添加-DOS_OBJECT_USE_OBJC=0到你的编译器标志禁用的Objective-C为基础的派遣对象。

GCD中存在三种队列:

1串行分发队列(Serial dispatch queue)串行分发队列又被称为私有分发队列,按顺序执行队列中的任务,且同一时间只执行一个任务。

 串行分发队列常用于实现同步锁。下面代码创建了一个串行分发队列:
  dispatch_queue_t serialQueue = dispatch_queue_create("com.example.MyQueue", NULL);

2并发分发队列(Concurrent dispatch queue)串行分发队列又被称为全局分发队列,也按顺序执行队列中的任务,但是顺序开始的多个任务会

 并发同时执行。并发分发队列常用于管理并发任务。下面代码创建了一个并发分发队列:
  dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

3主分发队列(Main dispatch queue)主分发队列是一个全局唯一的特殊的串行分发队列。队列中的任务会被在应用的主线程中执行。

主分发队列可以用于执行 UI 相关的操作。取得主分发队列的方法:dispatch_queue_t mainQueue = dispatch_get_main_queue();

GCD任务执行方式
GCD 中有两种任务执行方式:

异步执行, dispatch_async,意味将任务放入队列之后,主线程不会等待block的返回结果,而是立即继续执行下去。

同步执行, dispatch_sync,意味将任务放入队列之后,主线程被阻塞,需要等待block的执行结果返回,才能继续执行下去。 

整个队列的操作是异步还是阻塞取决于调用dispatch_async还是dispatch_sync,而队列中各任务的执行是一个个执行还是并发执行就取决于是用串行队列还是并发队列了,但不管是串行队列还是并发队列,只要是dispatch_sync调用的,队列里面的任务都会一个个执行。

(1)串行分发队列异步调用

在Async.m的实现

- (void)TestGCD1
{
    gFinish = NO;
    
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    dispatch_queue_t serialQueue = dispatch_queue_create("myQueue", NULL);
    for (int i = 0; i < 5; i ++) {
        dispatch_async(serialQueue, ^{
            if ([NSThread isMainThread]) {
                NSLog(@"task%d:当前在主线程中工作",i);
            }
            else
            {
                NSLog(@"task%d:当前在工作线程中工作",i);
            }
            sleep(1);
            if (i == 4) {
                gFinish = YES;
            }
        });
    }
    NSLog(@"串行分发队列异步执行");
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}

mian.mm中调用
TEST(Async, GCD1)
{
    Async *async = [[Async alloc] init];
    [async TestGCD1];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}
输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.GCD1

2015-10-20 16:03:24.004 test_asynchronous[1722:303] 当前在主线程中工作

2015-10-20 16:03:24.006 test_asynchronous[1722:1603] task0:当前在工作线程中工作

2015-10-20 16:03:24.006 test_asynchronous[1722:303] 串行分发队列异步执行

2015-10-20 16:03:25.008 test_asynchronous[1722:1603] task1:当前在工作线程中工作

2015-10-20 16:03:26.010 test_asynchronous[1722:1603] task2:当前在工作线程中工作

2015-10-20 16:03:27.012 test_asynchronous[1722:1603] task3:当前在工作线程中工作

2015-10-20 16:03:28.014 test_asynchronous[1722:1603] task4:当前在工作线程中工作

[       OK ] Async.GCD1 (8005 ms)

[----------] 1 test from Async (8005 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (8005 ms total)

[  PASSED  ] 1 test.

该队列是异步的,但每个任务都在同一个线程中按FIFO顺序执行。


(2)串行分发队列同步调用

在Async.m的实现

- (void)TestGCD2
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    dispatch_queue_t serialQueue = dispatch_queue_create("myQueue", NULL);
    for (int i = 0; i < 5; i ++) {
        dispatch_sync(serialQueue, ^{
            if ([NSThread isMainThread]) {
                NSLog(@"task%d:当前在主线程中工作",i);
            }
            else
            {
                NSLog(@"task%d:当前在工作线程中工作",i);
            }
            sleep(1);
            if (i == 4) {
                gFinish = YES;
            }
        });
    }
    NSLog(@"串行分发队列同步执行");
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}

在main.mm的实现

TEST(Async, GCD2)
{
    Async *async = [[Async alloc] init];
    [async TestGCD2];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}

输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.GCD2

2015-10-20 15:56:48.917 test_asynchronous[1679:303] 当前在主线程中工作

2015-10-20 15:56:48.919 test_asynchronous[1679:303] task0:当前在主线程中工作

2015-10-20 15:56:49.920 test_asynchronous[1679:303] task1:当前在主线程中工作

2015-10-20 15:56:50.923 test_asynchronous[1679:303] task2:当前在主线程中工作

2015-10-20 15:56:51.925 test_asynchronous[1679:303] task3:当前在主线程中工作

2015-10-20 15:56:52.926 test_asynchronous[1679:303] task4:当前在主线程中工作

2015-10-20 15:56:53.929 test_asynchronous[1679:303] 串行分发队列同步执行

[       OK ] Async.GCD2 (9014 ms)

[----------] 1 test from Async (9014 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (9014 ms total)

[  PASSED  ] 1 test.


同步阻塞主线程,队列中的任务依然是按FIFO顺序执行。

(3)并发分发队列异步调用
在Async.m中实现
- (void)TestGCD3
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (int i = 0; i < 5; i ++) {
        dispatch_async(concurrentQueue,
                       ^{
                           if ([NSThread isMainThread]) {
                               NSLog(@"task%d:当前在主线程中工作",i);
                           }
                           else
                           {
                               NSLog(@"task%d:当前在工作线程中工作",i);
                           }
                           sleep(1);
                           if (i == 4) {
                               gFinish = YES;
                           }
                       });
    }
    NSLog(@"并行分发队列异步执行");
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}
输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.GCD3

2015-10-20 16:00:26.541 test_asynchronous[1696:303] 当前在主线程中工作

2015-10-20 16:00:26.543 test_asynchronous[1696:303] 并行分发队列异步执行

2015-10-20 16:00:26.544 test_asynchronous[1696:1603] task0:当前在工作线程中工作

2015-10-20 16:00:26.544 test_asynchronous[1696:2003] task1:当前在工作线程中工作

2015-10-20 16:00:26.544 test_asynchronous[1696:2203] task3:当前在工作线程中工作

2015-10-20 16:00:26.544 test_asynchronous[1696:2103] task2:当前在工作线程中工作

2015-10-20 16:00:26.544 test_asynchronous[1696:2303] task4:当前在工作线程中工作

[       OK ] Async.GCD3 (4004 ms)

[----------] 1 test from Async (4005 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (4005 ms total)

[  PASSED  ] 1 test.

异步执行任务,队列中的任务是并发同时运行的,且每个任务的线程都不一样。

(4)并发分发队列
在Async.m 中实现
- (void)TestGCD4
{
    gFinish = NO;
    if ([NSThread isMainThread]) {
        NSLog(@"当前在主线程中工作");
    }
    else
    {
        NSLog(@"当前在工作线程中工作");
    }
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    for (int i = 0; i < 5; i ++) {
        dispatch_sync(concurrentQueue,
                       ^{
                           if ([NSThread isMainThread]) {
                               NSLog(@"task%d:当前在主线程中工作",i);
                           }
                           else
                           {
                               NSLog(@"task%d:当前在工作线程中工作",i);
                           }
                           sleep(1);
                           if (i == 4) {
                               gFinish = YES;
                           }
                       });
    }
    NSLog(@"并行分发队列同步执行");
    while (true)
    {
        sleep(4);
        if (gFinish)
        {
            return;
        }
    }
}
main.mm中调用
TEST(Async, GCD4)
{
    Async *async = [[Async alloc] init];
    [async TestGCD4];
}
int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}
输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.GCD4

2015-10-20 16:01:49.930 test_asynchronous[1709:303] 当前在主线程中工作

2015-10-20 16:01:49.933 test_asynchronous[1709:303] task0:当前在主线程中工作

2015-10-20 16:01:50.935 test_asynchronous[1709:303] task1:当前在主线程中工作

2015-10-20 16:01:51.937 test_asynchronous[1709:303] task2:当前在主线程中工作

2015-10-20 16:01:52.939 test_asynchronous[1709:303] task3:当前在主线程中工作

2015-10-20 16:01:53.941 test_asynchronous[1709:303] task4:当前在主线程中工作

2015-10-20 16:01:54.943 test_asynchronous[1709:303] 并行分发队列同步执行

[       OK ] Async.GCD4 (9015 ms)

[----------] 1 test from Async (9015 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (9015 ms total)

[  PASSED  ] 1 test.

尽管是并发队列,但只要是用同步调用里面的任务都是按FIFO顺序执行。

GCD中,都会有对应支持函数指针的方法,比如:
dispatch_async 对应 dispatch_async_f
dispatch_sync 对应 dispatch_sync_f
下面演示dispatch_async_f的使用:

(5)用函数指针来替换块语法,用dispatch_async_f
void Test()
{
    if ([NSThread isMainThread]) {
        NSLog(@"task%d:当前在主线程中工作");
    }
    else
    {
        NSLog(@"task%d:当前在工作线程中工作");
    }
    sleep(1);
}

- (void)TestGCD5
{
    gFinish = NO;
    int a = 1;
    if ([NSThread isMainThread]) {
        NSLog(@"task%d:当前在主线程中工作");
    }
    else
    {
        NSLog(@"task%d:当前在工作线程中工作");
    }
    dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_async_f(concurrentQueue, NULL, &Test);
    sleep(2);
}
main中运行:
TEST(Async, GCD5)
{
    Async *async = [[Async alloc] init];
    [async TestGCD5];
}

int main(int argc, char * argv[])
{
    testing::InitGoogleTest(&argc, argv);
    int res = RUN_ALL_TESTS();
    return res;
}
输出结果:

[==========] Running 1 test from 1 test case.

[----------] Global test environment set-up.

[----------] 1 test from Async

[ RUN      ] Async.GCD5

2015-10-21 15:43:35.240 test_asynchronous[1357:303] task-1880889215:当前在主线程中工作

2015-10-21 15:43:35.242 test_asynchronous[1357:1403] task-1880889215:当前在工作线程中工作

[       OK ] Async.GCD5 (2002 ms)

[----------] 1 test from Async (2002 ms total)


[----------] Global test environment tear-down

[==========] 1 test from 1 test case ran. (2003 ms total)

[  PASSED  ] 1 test.


总结:  

使用perform的理由:

  • 当你只想执行一个简单的异步操作而不需要控制任务状态。
  • 当只有较少异步操作的时候。

使用 NSOperation 的一些理由:

  • 当你需要取消线程任务时,GCD无法提供取消任务的操作。而NSOperation提供了取消任务的操作
  • 当你需要更细的粒度地观察任务改变了状态时,由于NSOperation是一个对象,比较GCD使用的block而言,通过对NSOperation对象进行键值观察(KVO)能很容易观察到任务的状态改变;
  • 当你需要重用线程任务时,NSOperation作为一个普通的Objective-C对象,可以存储任何信息。对象就是为重用而设计的,这时,NSOperationGCD使用的block要更方便

使用GCD的理由:

  • GCD 虽然在很多地方值得提倡,但并不是任务管理和多线程地唯一解决方案,并不是说所有的地方都应该使用 GCDGCD是一个纯C API,NSOperationObjective-C类,在一些地方对象编程是有优势的。NSOperation也提供了一些GCD无法实现,或者GCD所没有的功能。
  • Apple公司宣称其在GCD技术中为更好地利用多核硬件系统做了很多的优化。所以,在性能方面GCD是不用担心的。而且GCD也提供了相当丰富的API,几乎可以完成绝大部分线程相关的编程任务。所以,在多线程相关主题的编程中,GCD应该是首选。

参考了《os x 10.7官方文档》,还有一篇博客:http://www.infoq.com/cn/articles/os-x-ios-multithread-technology


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值