通过Grand Central Dispatch 优化程序

多线程编程被认为是黑色的艺术,即使是有经验的程序员,编写和维护多线程程序也是很困难和不快乐的。 所有也就尽可能的避免编写多线程程序。

多线程的目标是比较简单的:同一时间,竟可能的执行多个任务,比如在IOS用户操作时候,主线程负责绘制屏幕。 如果我们在主线程里面执行我们逻辑代码,界面线程可能不能及时的响应用户的输入。

所以呢,我们会把IO操作,复杂的计算,以及同步的网络请求。放到另外的线程中。有时候,使用多线程是没有办法的事情。

1 Grand Central Dispatch

GCD是苹果提供一种像多线程一样并行执行的技术,但是比多线程编程简单。程序员提交一个block代码到队列中,block的队列被操作系统控制和执行,以平行或者串行的形式。下面是个实例代码。

   dispatch_async(
	   dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"This is exectuted in a different thread") ;
	}) ;

通过调用dispatch_async我们来执行异步的任务。

 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);    有2个参数:  
*  `queue`,要执行任务的队列  
*  `block` 块

2 Dispatch Queues

我们可以使用ios提供的队列,来代替多线程操作,多线程有操作系统管理,所以这里不能够保证那个线程来执行我们的代码, image

这有2种队列:
1 并行队列 任务加入的队列是先进,先出的原则,操作系统自动生成四个不同级别并行dispatch queues,既然任务是冰心的执行,所以这里没有保障任务完成的顺序。

2 串行队列 在串行的队列中,只有上一个任务完成,下一个才会入队。串行的队列保证某一时刻只有一个任务执行。串行队列确保任务执行的按照预期的顺序执行。

所以IOS系统 全局派遣队列(global dispatch queues)是并行的。 如果要使用串行队列,我们需要动过dispatch_queue_create("queue_name", 0) 产生新队列,

3 queue 优先级

  • DISPATCH_QUEUE_PRIORITY_HIGH
  • DISPATCH_QUEUE_PRIORITY_DEFAULT
  • DISPATCH_QUEUE_PRIORITY_LOW
  • DISPATCH_QUEUE_PRIORITY_BACKGROUND
    上面的优先级逐次降低。

我们可以通过下面函数,来获得队列。

 dispatch_queue_t dispatch_get_global_queue(dispatch_queue_priority_t priority,unsigned long flags); 

4 main queue

当我们在主线程执行一些特殊的任务时候,向用户界面操作,main queue是非常有用的。比如更新label,显示信息,更新view,由于UI的操作室非线程安全的,所以我们必须在主线程中操作。 ` dispatch_async(dispatch_get_main_queue(), ^{NSLog(@”main thread”) ;}) ;`

例子

dispatch_queue_t mainQueue = dispatch_get_main_queue() ;
dispatch_async(mainQueue, ^(void){
    [[ [UIAlertView alloc] initWithTitle:@"Gcd" message:@"Gcd is running" delegate:self cancelButtonTitle:@"ok" otherButtonTitles:nil, nil] show] ;
});

5 顺序执行 dispatch_sync

dispatch_sync 确保提交到队列中,上一个任务执行完成后,才会执行下一个任务

void (^printFrom1To1000)(void) = ^ {
    NSUInteger counter = 0;
    for(counter =1; counter <= 5000 ; counter++)  {
        NSLog(@"counter = %lu --thread=%@" , (unsigned long) counter ,[NSThread currentThread]) ;
    }
    counter =0 ;
};


dispatch_queue_t concurrentQueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ;
dispatch_async(concurrentQueue, ^{
    dispatch_sync(concurrentQueue ,printFrom1To1000) ;
    dispatch_sync(concurrentQueue ,printFrom1To1000) ;

});

6 延时调用方法 dispatch_after

-(void) printString:(NSString*) paramString {
    NSLog(@"%@" , paramString) ;
}


- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
	//第一种方法
    [self performSelector:@selector(printString:) withObject:@"Grand Central Dispatch" afterDelay:5.0 ];
    
    double delayInSeconds = 5.0 ;
    //第二种方法
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"dispatch_after") ; 
    });
}

7 只调用一次 dispatch_once

static dispatch_once_t onceToken ;
void (^ executOnlyOnce) (void) = ^ {
    static NSUInteger numberofEntries = 0 ;
    numberofEntries++ ;
    
    NSLog(@"Exectuted %lu time(s)", (unsigned long) numberofEntries) ;
} ;

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    
    dispatch_queue_t concurrentQueue= dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) ;
    dispatch_once(&onceToken,^{
        dispatch_async(concurrentQueue, executOnlyOnce);
    }) ;
    
    dispatch_once(&onceToken,^{
        dispatch_async(concurrentQueue, executOnlyOnce);
    }) ;
}

8 dispatch_group

For each resource that needs loading, run a block that does the loading into memory, and run it in the background. Then run a subsequent block when all of the blocks have completed.

You can do this through dispatch groups, which are a way to submit multiple units of work to Grand Central Dispatch:

NSArray* imagesToLoad = [NSArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t backgroundQueue = dispatch_get_global_queue(
        DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0);

for (NSString* imageFileName in imagesToLoad)    { 
  dispatch_group_async(group, backgroundQueue, ^{
            // Load the file
 }); }

dispatch_queue_t mainQueue = dispatch_get_main_queue();

dispatch_group_notify(group, mainQueue, ^{
	// All images are done loading at this point
});