3-1 iOS 多线程 GCD

GCD 

如何用?

1.串行队列同步执行

    dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,同步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,====同步执行当前线程%@",i,[NSThread currentThread]);
        };
    });

2.串行队列异步执行

dispatch_queue_t queue = dispatch_queue_create("serialQueue", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
dispatch_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,====异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });

串行队列:任务一个一个按照顺序执行,同步和异步的区别在于是否会新开辟一条线程处理任务。但是串行队列仅仅只是开辟一条线程顺序处理任务。并不涉及到多条线程。


3.并发队列同步执行

dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,同步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
    dispatch_sync(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,-------同步执行当前线程%@",i,[NSThread currentThread]);
        };
    });

4.并发队列异步执行

dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
    dispatch_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,-------异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });

并发队列:并发队列可以用dispatch_get_global_queue()方法取得一个全局的并发队列,但如果有多个并发队列,请使用DISPATCH_QUEUE_CONCURRENT创建。并发队列异步执行,无法确定任务的执行顺序。并且会创建多条线程处理任务。

5.dispatch_barrier_async()
dispatch_queue_t queue = dispatch_queue_create("concurrentQueue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
dispatch_barrier_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,-------异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
dispatch_async(queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,========异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
dispatch_barrier_async(),一般用在并发队列中,他会先看队列中有没有其他的任务要执行,如果有的话,则等其他任务执行完,再执行,同时在此方法后添加的任务必须等待此方法中任务执行后才能执行。可以用来控制执行顺序。比如加载十张图片,你希望某张图片最先加载。将该图片的加载任务加入barrier_async中,其他图片加载任务放在async中。即可控制。

6.队列组 dispatch_group_t
dispatch_group_t groupQueue = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);//全局并发队列
    dispatch_group_async(groupQueue, queue, ^{
        for (int i = 0; i < 10; i ++) {
            NSLog(@"%d,异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
    dispatch_group_async(groupQueue, queue, ^{
        for (int i = 0; i < 1000; i ++) {
            NSLog(@"%d,----------异步执行当前线程%@",i,[NSThread currentThread]);
        };
    });
    dispatch_group_notify(groupQueue, queue, ^{
        NSLog(@"队列组中的任务全都执行完毕啦!");
    });

队列组:顾名思义,就是将所有队列加入一个组中,方便控制队列的启动和结束。比如,你需要下载n张图片,当n张图片都下载完成后,你需要把这n张图拼接成一张图片(或者弹个提示框,图片全部下载完毕)之类的。反正就是需要知道所有的任务都执行完毕。怎么做?1.bool值标记所有队列任务。2.用队列组。将队列加入group中,当所有任务执行完毕,group_notify回调通知。在这个block里面处理之后的任务。

7.dispatch_once()

static dispatch_once_t onceToken;
dispatch_once(&onceToken,^{
    /..../
});
单次执行一个任务,此方法中的任务只会执行一次,重复调用也没办法重复执行(单例模式中常用此方法)。


8.定时器

static dispatch_source_t _timer;
    NSTimeInterval period = 3.0; //设置时间间隔
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    dispatch_source_set_timer(_timer, dispatch_walltime(NULL, 0), period * NSEC_PER_SEC, 0); //period秒执行
    // 事件回调
    dispatch_source_set_event_handler(_timer, ^{
        dispatch_async(dispatch_get_main_queue(), ^{
            /任务,看你自己的需求.../
        });
    });
    
    // 开启定时器
    dispatch_resume(_timer);
    
    // 关闭定时器
    // dispatch_source_cancel(_timer);


线程锁,资源抢占问题

首先,为什么有线程锁这个概念?什么情况会发生资源抢占问题?不加锁有什么后果?

假如,我们有一个数据库,存储了十条数据,现在有十五条线程,去访问这十条数据(数据量很大)。这就出现了资源竞争的情况,如何保证这十条数据都能确切的被访问到?

为了直观的描述这个问题,上代码

//初始化锁对象
    //_lock=[[NSLock alloc]init];
    _dataArray = [NSMutableArray arrayWithCapacity:10];
    for (int i = 0; i < 10; i ++) {
        [_dataArray addObject:@(2)];
    }
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    for (int i = 0; i < 15; i ++) {
        dispatch_async(queue, ^{
//            NSLog(@"我是第%d条线程",i+1);
            [self handleData:i];
        });
    }


- (void)handleData:(int)cout {
    //先判断_dataArray是否有数据,如果有,则进行读取
    //[_lock lock];
    if (_dataArray.count > 0) {
        
        NSLog(@"第%d条线程访问了这个数据,_dataArray有%lu个数据",cout+1,(unsigned long)_dataArray.count);
        [NSThread sleepForTimeInterval:[[_dataArray lastObject] intValue]];//模拟读文件,因为不好找这么大的文件,用这个代替,相当于读取_dataArray的lastObj,耗费了几秒钟。
        [_dataArray removeLastObject];
        
    }
    //[_lock unlock];
}

不加锁的打印情况:

1.sleep2秒


因为_dataArray数据抢占问题,最后一个元素被多条线程持有,remove崩溃。

2.sleep0秒



可以很直观的看到,多条线程访问了同一个数据!如果这个数据比较重要,比如火车票,那多个人都定了这张票,给谁好??

所以。对于这种资源抢占的情况,我们必须要对这段代码加锁。保证这段代码在某个线程中执行完之后,才能被另外一个线程访问。

打开[_lock lock unlock]注释即可。

或者采用@synchronized的方式加同步锁

@synchronized(self) {
        if (_dataArray.count > 0) {
            
            NSLog(@"第%d条线程访问了这个数据,_dataArray有%lu个数据",cout+1,(unsigned long)_dataArray.count);
            //        [NSThread sleepForTimeInterval:[[_dataArray lastObject] intValue]];//模拟读文件,因为不好找这么大的文件,用这个代替,相当于读取_dataArray的lastObj,耗费了几秒钟。
            [_dataArray removeLastObject];
            
        }
    }





你可能感兴趣的:(IOS-开发)