ios多线程---GCD详解

在iOS里实现多线程的技术有很多,使用起来最简单的是GCD是系统帮你开辟线程,执行效率最高的也是GCD,是相对底层的API,都是C的函数。GCD是苹果最推荐的多线程技术,GCD的核心是往dispatch queue里添加要执行的任务,由queue管理任务的执行。(注:之所以程序中会用到多线程是因为程序往往会需要读取数据,然后更新UI.为了良好的用户体验,读取数据的操作会倾向于在后台运行,这样以避免阻塞主线程.所以我们就可以将这些读取数据的操作方在子线程中,UI的更新放在主线程中执行)

dispatch queue有两种:1.serial queue(串行队列) 2.concurrent queue(并行队列)无论哪种队列都是FIFO(先进先出)

serial queue的特点:执行完queue中第一个任务,执行第二个任务,执行完第二个任务,执行第三个任务,以此类推,任何一个任务的执行,必须等到上个任务执行完毕。

获取串行队列的方法有两种:

1.获得mainQueue。mainQueue会在主线程中执行,即:主线程中执行队列中的各个任务

    dispatch_queue_t mainQueue = dispatch_get_main_queue();

    dispatch_async(mainQueue, ^{

        NSLog(@"1个任务");//block里写要执行的任务(代码)

    dispatch_async(mainQueue, ^{

        NSLog(@"2个任务");//block里写要执行的任务(代码)

    dispatch_async(mainQueue, ^{

        NSLog(@"3个任务");

    });//block里写要执行的任务(代码)

2、自己创建serial queue。自己创建的serial queue不会在主线程中执行,queue会开辟一个子线程,在子线程中执行队列中的各个任务

    dispatch_queue_t mySerialQueue = dispatch_queue_create("com.myCompany.GCD.mySerialQueue",  DISPATCH_QUEUE_SERIAL);//queue起名字的时候,苹果推荐使用反向域名格式。

    dispatch_async(mySerialQueue, ^{

        NSLog(@"1个任务");

    });//block里写要执行的任务(代码)

    dispatch_async(mySerialQueue, ^{

        NSLog(@"2个任务");

    });//block里写要执行的任务(代码)

    dispatch_async(mySerialQueue, ^{

        NSLog(@"3个任务");

        dispatch_async(dispatch_get_main_queue(), ^{

            NSLog(@"主线程要执行的操作");

       });//回到主线程进行的操作,回到主线程中进行操作可按照你的实际需要进行

    });//block里写要执行的任务(代码)

   

concurrent queue特点:队列中的任务,第一个先执行,不等第一个执行完毕,第二个就开始执行了,不等第二个任务执行完毕,第三个就开始执行了,以此类推。后面的任务执行的晚,但是不会等前面的执行完才执行。

获得concurrent queue的方法有2种:

1.获得global queue。

dispatch_queue_tglobalQueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);//第一个参数控制globalQueue的优先级,一共有4个优先级DISPATCH_QUEUE_PRIORITY_HIGH、DISPATCH_QUEUE_PRIORITY_DEFAULT、DISPATCH_QUEUE_PRIORITY_LOW、DISPATCH_QUEUE_PRIORITY_BACKGROUND。第二个参数是苹果预留参数,未来会用,目前填写为0.

global queue会根据需要开辟若干个线程,并行执行队列中的任务(开始较晚的任务未必最后结束,开始较早的任务未必最先完成),开辟的线程数量取决于多方面因素,比如:任务的数量,系统的内存资源等等,会以最优的方式开辟线程---根据需要开辟适当的线程。

    dispatch_async(globalQueue, ^{

        NSLog(@"1个任务");

    });//block里写要执行的任务(代码)

    dispatch_async(globalQueue, ^{

        NSLog(@"2个任务");

    });//block里写要执行的任务(代码)

    dispatch_async(globalQueue, ^{

        NSLog(@"3个任务");

    });//block里写要执行的任务(代码)

2.自己创建concurrent queue。

自己创建的concurrent queue会根据需要开辟若干个线程,并行执行队列中的任务(开始较晚的任务未必最后结束,开始较早的任务未必最先完成),开辟的线程数量取决于多方面因素,比如:任务的数量,系统的内存资源等等,会以最优的方式开辟线程---根据需要开辟适当的线程。

     dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.myCompany.GCD.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"1个任务");

    });//block里写要执行的任务(代码)

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"2个任务");

    });//block里写要执行的任务(代码)

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"3个任务");

    });//block里写要执行的任务(代码)

好了,以上就是队列的两种方式,下面我和大家介绍一下任务的分组执行

dispatch_group_async用于把不同的任务归为一组

dispatch_group_notify当指定组的任务执行完毕之后,执行给定的任务

代码如下:

    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);

    dispatch_group_t group1 = dispatch_group_create();

    dispatch_group_async(group1, queue, ^{

        NSLog(@"A买水果");

    });

    dispatch_group_async(group1, queue, ^{

        NSLog(@"B买烤鸭");

    });

    dispatch_group_async(group1, queue, ^{

        NSLog(@"C买扑克");

    });

    dispatch_group_async(group1, queue, ^{

        NSLog(@"D买水");

    });

    dispatch_group_notify(group1, queue, ^{

        NSLog(@"Let's go!");  // 只有group1这一组的全部完成时,才会执行这个notify

    });

打印结果如下:
ios多线程---GCD详解_第1张图片

为了保证访问同一个数据时,数据的安全,我们可以使用serial queue解决数据安全访问的问题。

serial queue的缺陷是:后面的任务 必须等待 前面的任务执行完毕才能执行,对于往数据库写入数据 使用serial queue无疑能保证数据的安全。对于从数据库中读取数据,使用serial queue就不太合适了,效率比较低。使用concurrent queue无疑是最合适的。真实的项目中,通常既有对数据库的写入,又有数据库的读取。如何处理才最合适呢?

下面给出了 既有数据库数据读取,又有数据库数据写入的处理方法dispatch_barrier_async

dispatch_queue_t myConcurrentQueue = dispatch_queue_create("com.myCompany.GCD.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"读取一些数据");

    });

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"读取另外一些数据");

    });

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"读取这些数据");

    });

    

    dispatch_barrier_async(myConcurrentQueue, ^{

        NSLog(@"写入某些数据");

    });//dispatch_barrier_async就像一道墙,之前的任务都并行执行,执行完毕之后,执行barrier中的任务,之后的任务也是并行执行。

    

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"读取XXX数据");

    });

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"读取OOXX数据");

    });

    dispatch_async(myConcurrentQueue, ^{

        NSLog(@"读取aaa数据");

    });

打印结果如下:

ios多线程---GCD详解_第2张图片


dispatch_once 用于定义那些只需要执行一次的代码,比如单例的创建

    static dispatch_once_t onceToken;

    dispatch_once(&onceToken, ^{

        NSLog(@"只执行一次");

        //这个block里的代码,在程序执行过程中只会执行一次。

        //比如在这里某些单例的初始化

        //static YourClass *instance = nil;

        //instance = [[YourClass alloc] init];

    });


我们一直在用  dispatch_async,GCD里有一些API是dispatch_sync,二者有什么区别呢?

1.dispatch_async无需等block执行完,继续执行dispatch_async后面的代码

2.dispatch_sync必须等block执行完,才继续执行dispatch_sync后面的代码

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

//    dispatch_sync(queue, ^{

//        for (int i = 0; i < 10; i++) {

//            NSLog(@"%d",i);

//        }

//    });

//    NSLog(@"haha");

    dispatch_async(queue, ^{

        for (int i = 0; i < 10; i++) {

            NSLog(@"%d",i);

        }

    });

    NSLog(@"haha");

打印结果如下:
ios多线程---GCD详解_第3张图片
ios多线程---GCD详解_第4张图片

下面介绍的这个方法我也不常用,不过拿出来和大家一起分享一下吧

- (IBAction)functionPoint:(id)sender {

    //dispatch_async_f往队列里放函数指针,队列控制相应函数的执行,不在是控制block的执行

    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    //    dispatch_async_f(<#dispatch_queue_t queue#>, <#void *context#>, <#dispatch_function_t    work#>);

    dispatch_async_f(queue, @"你好", function);//函数指针对应的函数类型:必须没有返回值,参数必须是void *。函数指针对应的参数,由dispatch_async_f第二个参数提供,可以是任意对象类型。

}

void function(void *context)

{

    NSLog(@"%@",context);

}

打印结果如下:


希望对大家有所帮助,欢迎大家留言,共同交流哈。








你可能感兴趣的:(多线程)