IOS多线程之GCD

相比较NSOperation和NSThread,GCD提供了更简单的操作实现多线程,多线程下也无需创建自动释放池,而且GCD开发只有两个步骤

1 创建队列

2 提交任务到队列

队列

GCD创建的队列有两种,一种是串行队列,一种是并行队列,在串行队列中每次只执行一个任务,依次执行下去,而在并行队列中每次可以同时执行多个任务

//获取当前的队列
dispatch_queue_t queue = dispatch_get_current_queue();

可以通过以下两种方式可以获得串行队列

dispatch_queue_t serialqueue1 = dispatch_get_main_queue();
    
dispatch_queue_t serialqueue2 = dispatch_queue_create("queue", DISPATCH_QUEUE_SERIAL);
//获取队列字符串标签
char *label = dispatch_queue_get_label(serialqueue2);

第一种方式获取主线程关联的队列,第二种方式自己创建了一个串行队列

通过以下两种方式可以获得并行队列

    //获取系统全局并发队列
    //第一个参数接受DISPATCH_QUEUE_PRIORITY_HIGH  DISPATCH_QUEUE_PRIORITY_DEFAULT DISPATCH_QUEUE_PRIORITY_LOW DISPATCH_QUEUE_PRIORITY_BACKGROUND
    //第二个参数为了以后扩展,只需传入0
    dispatch_queue_t concurrent1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    //自己创建并发队列
    dispatch_queue_t concurrent2 = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);

任务提交

任务提交有两种方式,同步提交和异步提交

可以使用代码块或者函数的方式实现任务的异步提交

当我们把任务异步提交到并行队列,主线程串行队列以及自定义串行队列中时会有以下不同的结果

异步提交代码一:(自定义串行队列)

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    

    //创建串行队列
    dispatch_queue_t serialqueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);//异步提交到串行队列
    dispatch_async(serialqueue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    
    dispatch_async(serialqueue, ^{
        NSLog(@"%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
   [NSThread sleepForTimeInterval:
2]; NSLog(@"main:%@",[NSThread currentThread]); }

2014-10-31 15:39:06.242 IOS多线程[15238:1403] <NSThread: 0x8e39b60>{name = (null), num = 2}

2014-10-31 15:39:08.243 IOS多线程[15238:90b] main:<NSThread: 0x8b4bf00>{name = (null), num = 1}

2014-10-31 15:39:09.243 IOS多线程[15238:1403] <NSThread: 0x8e39b60>{name = (null), num = 2}

分析:上面代码一共有两条队列,我们自己创建的串行队列以及主线程队列,异步提交dispatch_async会立即返回不会阻塞主线程后面的代码,所以两条队列异步执行,又因为serialqueue是串行队列,所以我们提交的任务依次执行。

 

异步提交代码二:(主线程串行队列)

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    //异步提交到串行队列
    dispatch_async(mainqueue, ^{
        NSLog(@"task1:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    
    dispatch_async(mainqueue, ^{
        NSLog(@"task2:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    [NSThread sleepForTimeInterval:2];
    NSLog(@"main:%@",[NSThread currentThread]);
    
}

2014-10-31 16:00:02.820 IOS多线程[15350:90b] main:<NSThread: 0x8b799c0>{name = (null), num = 1}

2014-10-31 16:00:02.835 IOS多线程[15350:90b] task1:<NSThread: 0x8b799c0>{name = (null), num = 1}

2014-10-31 16:00:05.837 IOS多线程[15350:90b] task2:<NSThread: 0x8b799c0>{name = (null), num = 1}

分析:异步添加两个任务到主线程队列(串行队列),dispatch_async会立刻返回,主线程程序执行完后会依次执行添加到主线程队列的任务

 

异步提交代码三(并行队列)

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //创建并发队列
    dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
    
    
    //异步提交到串行队列
    dispatch_async(concurrentqueue, ^{
        NSLog(@"task1:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    
    dispatch_async(concurrentqueue, ^{
        NSLog(@"task2:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    [NSThread sleepForTimeInterval:2];
    NSLog(@"main:%@",[NSThread currentThread]);

}

2014-10-31 16:05:03.272 IOS多线程[15385:3607] task2:<NSThread: 0x8b76200>{name = (null), num = 3}

2014-10-31 16:05:03.272 IOS多线程[15385:1403] task1:<NSThread: 0x8e29f00>{name = (null), num = 2}

2014-10-31 16:05:05.273 IOS多线程[15385:90b] main:<NSThread: 0x8b3c2a0>{name = (null), num = 1}

分析:上面创建了两个队列,异步提交dispatch_async立即返回不会阻塞主线程后面的代码,所以两个队列异步执行,又因为我们创建的队列是并行队列,所以我们提交到队列中的两个任务也会并行执行

 

同步提交也有代码块和函数两种方式

我们把任务同步提交给并行队列,主线程队列以及自定义队列时会有以下不同结果

同步提交代码一:(自定义串行队列)

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //创建串行队列
    dispatch_queue_t serialqueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
    
    //同步提交到串行队列
    dispatch_sync(serialqueue, ^{
        NSLog(@"task1:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    
    dispatch_sync(serialqueue, ^{
        NSLog(@"task2:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    [NSThread sleepForTimeInterval:2];
    NSLog(@"main:%@",[NSThread currentThread]);

}

2014-10-31 16:17:50.113 IOS多线程[15438:90b] task1:<NSThread: 0x8b7cfb0>{name = (null), num = 1}

2014-10-31 16:17:53.114 IOS多线程[15438:90b] task2:<NSThread: 0x8b7cfb0>{name = (null), num = 1}

2014-10-31 16:17:58.117 IOS多线程[15438:90b] main:<NSThread: 0x8b7cfb0>{name = (null), num = 1}

分析:因为使用了同步提交dispatch_sync,该函数只有当提交的任务执行完才会返回继续执行后面的代码

 

同步提交代码二:(主线程串行队列)

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    
    //同步提交到串行队列
    dispatch_sync(mainqueue, ^{
        NSLog(@"task1:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    
    dispatch_sync(mainqueue, ^{
        NSLog(@"task2:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    [NSThread sleepForTimeInterval:2];
    NSLog(@"main:%@",[NSThread currentThread]);

} 

分析:以上代码是错误的会产生死锁,因为通过dispatch_sync提交任务会等执行完才返回,而任务又被提交到了主线程串行队列,所以会先等待主线程代码执行完,主线程又在等待dispatch_sync返回,两者相互等待对方执行,所以产生了死锁。

 

同步提交代码三:(并行队列)

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //创建并发队列
    dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);
    
    
    //同步提交到串行队列
    dispatch_sync(concurrentqueue, ^{
        NSLog(@"task1:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    
    dispatch_sync(concurrentqueue, ^{
        NSLog(@"task2:%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3];
    });
    
    [NSThread sleepForTimeInterval:2];
    NSLog(@"main:%@",[NSThread currentThread]);

}

2014-10-31 16:21:01.445 IOS多线程[15453:90b] task1:<NSThread: 0x8b769a0>{name = (null), num = 1}

2014-10-31 16:21:04.447 IOS多线程[15453:90b] task2:<NSThread: 0x8b769a0>{name = (null), num = 1}

2014-10-31 16:21:09.449 IOS多线程[15453:90b] main:<NSThread: 0x8b769a0>{name = (null), num = 1}

分析:因为使用了同步提交dispatch_sync,该函数只有当提交的任务执行完才会返回继续执行后面的代码,所以当使用同步提交时,并行队列和自定义的串行队列作用是一样的

 

GCD其他常用方法

 执行多个操作:dispatch_apply

- (void)viewDidLoad
{
    [super viewDidLoad];
    // Do any additional setup after loading the view.

    //串行队列
    dispatch_queue_t serialqueue = dispatch_queue_create("serialqueue", DISPATCH_QUEUE_SERIAL);
    
    //主线程队列
    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    
    //并发队列
    dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);

    
    dispatch_apply(5, concurrentqueue, ^(size_t time) {
        NSLog(@"第%zu此执行,%@",time,[NSThread currentThread]);
    });
    
    NSLog(@"main");
}

输出

2014-10-31 19:44:26.816 IOS多线程[15709:3a03] 3此执行,<NSThread: 0x9b68ce0>{name = (null), num = 4}

2014-10-31 19:44:26.816 IOS多线程[15709:1403] 0此执行,<NSThread: 0x8b7a410>{name = (null), num = 2}

2014-10-31 19:44:26.816 IOS多线程[15709:90b] 1此执行,<NSThread: 0x8b7a2d0>{name = (null), num = 1}

2014-10-31 19:44:26.816 IOS多线程[15709:3903] 2此执行,<NSThread: 0x996a630>{name = (null), num = 3}

2014-10-31 19:44:26.817 IOS多线程[15709:3a03] 4此执行,<NSThread: 0x9b68ce0>{name = (null), num = 4}

2014-10-31 19:44:26.818 IOS多线程[15709:90b] main

如果把dispatch_apply队列换成串行队列输出如下

2014-10-31 19:52:07.125 IOS多线程[15724:90b] 0此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}

2014-10-31 19:52:07.126 IOS多线程[15724:90b] 1此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}

2014-10-31 19:52:07.126 IOS多线程[15724:90b] 2此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}

2014-10-31 19:52:07.126 IOS多线程[15724:90b] 3此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}

2014-10-31 19:52:07.127 IOS多线程[15724:90b] 4此执行,<NSThread: 0x8b28ad0>{name = (null), num = 1}

2014-10-31 19:52:07.127 IOS多线程[15724:90b] main

如果换成主线程队列则会产生死锁

 

只执行一次任务(可以用来实现单例模式):

static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
    //执行代码块
});

onceToken用来判断代码块是否已经执行过,它的本质是一个long型整数

 

指定时间点执行任务:

dispatch_after(dispatch_time_t when,dispatch_queue_t queue,dispatch_block_t block); 

 

分组:

如果希望队列中所有任务结束后执行某个操作(比如执行3个下载任务,全部完成后通知界面更新),那么可以使用分组

    //主线程队列
    dispatch_queue_t mainqueue = dispatch_get_main_queue();
    
    //并发队列
    dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);

    
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, concurrentqueue, ^{
        NSLog(@"task1");
    });
    dispatch_group_async(group, concurrentqueue, ^{
        NSLog(@"task2");
    });
    dispatch_group_notify(group, mainqueue, ^{
        NSLog(@"updateui");
    });

上面代码当task1和task2都执行完后才会执行notify中的任务

分界:

分界是指同一队列中当前面添加的任务执行完才会执行分界任务,而且只有当分界任务执行完才会执行后面添加的任务

    dispatch_queue_t concurrentqueue = dispatch_queue_create("concurrentqueue", DISPATCH_QUEUE_CONCURRENT);

    
    dispatch_async(concurrentqueue, ^{
        NSLog(@"task1");
    });
    
    dispatch_async(concurrentqueue, ^{
        NSLog(@"task2");
    });
    
    dispatch_barrier_async(concurrentqueue, ^{
        NSLog(@"barriertasck");
    });
    
    dispatch_async(concurrentqueue, ^{
        NSLog(@"lasttask");
    });

 上面代码只有当task1和task2执行完才会执行分解任务barriertask,当分解任务执行完才会执行lasttask

原文地址:https://www.cnblogs.com/zanglitao/p/4065344.html