iOS 并发队列

一、GCD

GCD是apple开发中并发队列(多线程)的一个解决方案。他主要用于优化应用程序以支持多核处理器。GCD用非常简洁的方法,实现极为复杂的多线程编程。GCD是纯C的api 在ios4.0时推出。
例如

 dispatch_async(dispatch_get_global_queue(0, 0), ^{
       //在子线程执行的代码,长时间处理
        
        dispatch_async(dispatch_get_main_queue(), ^{
                //主线程刷新UI
        });
    });

上述代码中,将需要长时间运行的操作代码放入子线程,执行完毕后回到主线程继续使用。

一、GCD队列

1、dispatch_get_main_queue 主队列,在主线程执行代码。
2、dispatch_get_global_queue 全局队列。 可以设置优先级和tag
以上2个钟队列属于开箱既用
3、dispatch_queue_t queue = dispatch_queue_create(“name”, DISPATCH_QUEUE_SERIAL); DISPATCH_QUEUE_SERIAL等同于NULL 串行队列,多个任务将顺序被执行
4、dispatch_queue_create(“name”,DISPATCH_QUEUE_CONCURRENT) 并行队列,允许多个任务同时被执行。

二、GCD执行方式
1、dispatch_sync 同步执行。按顺序执行队列,执行完毕后向下继续执行下一段任务。
2、dispatch_async 异步执行。 除在主队列外,只有任务就会获取子线程。 在串行队列时一个接一个任务的进行调度,在并行队列时可以同时调度多个任务。

使用dispatch_queue_create创建的队列,在MRC下应使用dispatch_release()进行释放销毁。

线程调度对比表

 

 

三、如果希望多个线程并发执行,都执行完之后通知进行后续处理:


    dispatch_queue_t dispatchQueue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);
    dispatch_group_t dispatchGroup = dispatch_group_create();
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        //任务一
    });
    dispatch_group_async(dispatchGroup, dispatchQueue, ^(){
        //任务二
    });
    dispatch_group_notify(dispatchGroup, dispatch_get_main_queue(), ^(){
        //上面的任务都执行完了哦
    });

其他GCD相关API:
1、dispatch_once 单次执行,适用于单例模式创建等,即使代码被多次调用,里面的也只会被执行一次,例如

+(DBManager *)sharedInstance  
{  
    static DBManager *sharedManager;  
      
    static dispatch_once_t onceToken;  
    dispatch_once(&onceToken, ^{  
        sharedManager = [[DBManager alloc] init];  
    });  
      
    return sharedManager;  
}  

2、dispatch_apply 重复多次调用

dispatch_queue_t queue = dispatch_queue_create("queue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_apply(5, queue, ^(size_t i) {
  //执行代码
    }); 

在上述并行队列中 会在多个线程执行,如在串行队列中将会在一个线程中顺序执行。

3、dispatch_time 延迟执行,例如

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{
  //执行代码
});

注意:时间单位为纳秒

4、dispatch_barrier_async

 dispatch_queue_t queue = dispatch_queue_create("name", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"1%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2%@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"barrier%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"3%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"4%@", [NSThread currentThread]);
    });

以上代码中, 12之间的执行顺序不定, 34之间的执行顺序也不定,但在1和2都执行完之后会执行barrier 然后继续并行执行3和4

二、NSOperation

1、NSOperation是在ios2.0推出,在4.0推出GCD之后重新实现,底层基于GCD。是面向对象的编程方式。
2、NSOperation是一个抽象的基类,可以为子类提供有用且线程安全的建立状态(ready cancelled executing finshed),优先级,依赖和取消等操作。系统已经给我们封装了NSBlockOperation和NSInvocationOperation这两个实体类。
3、NSOperation的执行:operation 可以手动调用start方法进行执行,这个方法会在当前线程以同步的方式进行执行。 也可以创建NSOperationQueue对象 然后使用addOperation:系统将会自动执行该队列中的operation
4、队列最大并发数 maxConcurrentOperationCount
5、队列的操作
a取消所有操作 cancelAllOperations 也可以对operation单独调用cancel方法
b暂停和恢复队列 setSupended:(BOOl) yes代表暂停 no代表恢复
-(BOOL) isSupended
6、操作依赖
NSOperation之间可以设置依赖关系用来保证执行顺序。比如B执行完毕才能执行A (且看下列代码)
(注意可以多级依赖或者一个操作依赖多个操作 但不能相互依赖)

-(void) demo1
{
    NSLog(@"B");
}



-(void) run{
    
    NSBlockOperation *operA = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"1");
    }];
    
    NSInvocationOperation* operB = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(demo1) object:nil];
    
    NSOperationQueue*  queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 3; //设置最大并行数
    
    [operA addDependency:operB]; //设置依赖关系
    
    [queue addOperation:operA];
    [queue addOperation:operB];
}

三、多线程安全问题

1、@synchronized

NSObject *obj = [[NSObject alloc] init];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    @synchronized(obj) {
       NSLog(@"需要线程同步的操作1 开始");
       sleep(3);
       NSLog(@"需要线程同步的操作1 结束");
    }
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
    sleep(1);
    @synchronized(obj) {
       NSLog(@"需要线程同步的操作2");
    }
});    

注意,此处的obj必须为2个线程可以共用的变量,否则将失去作用,如果此时使用self则阻塞不起作用。

2、NSLock 是我们经常所使用的,除lock和unlock方法外,NSLock还提供了tryLock和lockBeforeDate:两个方法,tryLock会尝试加锁,如果锁不可用(已经被锁住),刚并不会阻塞线程,并返回NO。lockBeforeDate:方法会在所指定Date之前尝试加锁,如果在指定时间之前都不能加锁,则返回NO。

3、NSRecursiveLock 实际上定义的是一个递归锁,这个锁可以被同一线程多次请求,而不会引起死锁。这主要是用在循环或递归操作中。

4、NSConditionLock 有时一把只会lock和unlock的锁未必就能完全满足我们的使用。因为普通的锁只能关心锁与不锁,而不在乎用什么钥匙才能开锁,而我们在处理资源共享的时候,多数情况是只有满足一定条件的情况下才能打开这把锁

5、pthread_mutex C下的多线程锁,不多做介绍。

6、OSSpinLock自旋锁 和NSLock使用方式基本相同。 OC中原子操作用的就是自旋锁。自旋锁的等待过程一直使用do while循环的方式进行(而非线程间的调度),会有大量性能开销。

外链:
iOS中保证线程安全的几种方式与性能对比