您的位置:首页 > 移动开发 > IOS开发

IOS开发之多线程队列

2015-08-01 18:49 309 查看

串行队列

特点

先进先出
的方式,
顺序
调度队列中的任务执行
无论队列中所指定的执行任务函数是同步还是异步,都会等待前一个任务执行完成后,再调度后面的任务

队列创建

dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_SERIAL);dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", NULL);

串行队列演练

串行队列 同步执行

/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo1 {    // 1. 队列    dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_SERIAL);    // 2. 执行任务    for (int i = 0; i < 10; ++i) {        NSLog(@"--- %d", i);        dispatch_sync(q, ^{            NSLog(@"%@ - %d", [NSThread currentThread], i);        });    }    NSLog(@"come here");}

串行队列 异步执行

/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo2 {    // 1. 队列    dispatch_queue_t q = dispatch_queue_create("itheima", NULL);    // 2. 执行任务    for (int i = 0; i < 10; ++i) {        NSLog(@"--- %@ %d", [NSThread currentThread], i);        dispatch_async(q, ^{            NSLog(@"%@ - %d", [NSThread currentThread], i);        });    }    NSLog(@"come here");}

并发队列

特点

先进先出
的方式,
并发
调度队列中的任务执行
如果当前调度的任务是
同步
执行的,会等待任务执行完成后,再调度后续的任务
如果当前调度的任务是
异步
执行的,同时底层线程池有可用的线程资源,会再新的线程调度后续任务的执行

队列创建

dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT);

并发队列演练

并发队列 异步执行

/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo3 {    // 1. 队列    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);    // 2. 执行任务    for (int i = 0; i < 10; ++i) {        dispatch_async(q, ^{            NSLog(@"%@ - %d", [NSThread currentThread], i);        });    }    NSLog(@"come here");}

并发队列 同步执行

/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo4 {    // 1. 队列    dispatch_queue_t q = dispatch_queue_create("itheima", DISPATCH_QUEUE_CONCURRENT);    // 2. 执行任务    for (int i = 0; i < 10; ++i) {        dispatch_sync(q, ^{            NSLog(@"%@ - %d", [NSThread currentThread], i);        });        NSLog(@"---> %i", i);    }    NSLog(@"come here");}

主队列

特点

专门用来在主线程上调度任务的队列
不会开启线程
先进先出
的方式,在
主线程空闲时
才会调度队列中的任务在主线程执行
如果当前主线程正在有任务执行,那么无论主队列中当前被添加了什么任务,都不会被调度

队列获取

主队列是负责在主线程调度任务的
会随着程序启动一起创建
主队列只需要获取不用创建

dispatch_queue_t queue = dispatch_get_main_queue();

主队列演练

主队列,异步执行

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    [self gcdDemo1];    [NSThread sleepForTimeInterval:1];    NSLog(@"over");}- (void)gcdDemo1 {    dispatch_queue_t queue = dispatch_get_main_queue();    for (int i = 0; i < 10; ++i) {        dispatch_async(queue, ^{            NSLog(@"%@ - %d", [NSThread currentThread], i);        });        NSLog(@"---> %d", i);    }    NSLog(@"come here");}2015-07-13 00:44:57.241 testGCD线程[37988:581895] ---> 02015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 12015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 22015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 32015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 42015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 52015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 62015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 72015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 82015-07-13 00:44:57.242 testGCD线程[37988:581895] ---> 92015-07-13 00:44:57.242 testGCD线程[37988:581895] come here2015-07-13 00:44:58.243 testGCD线程[37988:581895] over2015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 02015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 12015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 22015-07-13 00:44:58.244 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 32015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 42015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 52015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 62015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 72015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 82015-07-13 00:44:58.245 testGCD线程[37988:581895] <NSThread: 0x7facd160e9a0>{number = 1, name = main} - 9

主线程空闲时
才会调度队列中的任务在主线程执行


主队列,同步执行

// MARK: 主队列,同步任务- (void)gcdDemo6 {    // 1. 队列    dispatch_queue_t q = dispatch_get_main_queue();    NSLog(@"!!!");    // 2. 同步    dispatch_sync(q, ^{        NSLog(@"%@", [NSThread currentThread]);    });    NSLog(@"come here");}

主队列
主线程
相互等待会造成死锁


同步任务的作用

同步任务,可以让其他异步执行的任务,
依赖
某一个同步任务


例如:在用户登录之后,再异步下载文件!

- (void)gcdDemo1 {    dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT);    dispatch_sync(queue, ^{        NSLog(@"登录 %@", [NSThread currentThread]);    });    dispatch_async(queue, ^{        NSLog(@"下载 A %@", [NSThread currentThread]);    });    dispatch_async(queue, ^{        NSLog(@"下载 B %@", [NSThread currentThread]);    });}

代码改造,让登录也在异步执行

- (void)gcdDemo2 {    dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT);    void (^task)() = ^{        dispatch_sync(queue, ^{            NSLog(@"登录 %@", [NSThread currentThread]);        });        dispatch_async(queue, ^{            NSLog(@"下载 A %@", [NSThread currentThread]);        });        dispatch_async(queue, ^{            NSLog(@"下载 B %@", [NSThread currentThread]);        });    };    dispatch_async(queue, task);}

主队列调度同步队列不死锁

- (void)gcdDemo3 {    dispatch_queue_t queue = dispatch_queue_create("com.itheima.queue", DISPATCH_QUEUE_CONCURRENT);    void (^task)() = ^ {        dispatch_sync(dispatch_get_main_queue(), ^{            NSLog(@"死?");        });    };    dispatch_async(queue, task);}

主队列在
主线程空闲时
才会调度队列中的任务在主线程执行

Barrier 异步


主要用于在多个异步操作完成之后,统一对
非线程安全
的对象进行更新
适合于
大规模的 I/O
操作

代码演练

准备工作

@interface ViewController () {    // 加载照片队列    dispatch_queue_t _photoQueue;}@property (nonatomic, strong) NSMutableArray *photoList;@end- (NSMutableArray *)photoList {    if (_photoList == nil) {        _photoList = [[NSMutableArray alloc] init];    }    return _photoList;}

NSMutableArray
是非线程安全的


viewDidLoad

- (void)viewDidLoad {    [super viewDidLoad];    _photoQueue = dispatch_queue_create("com.itheima.com", DISPATCH_QUEUE_CONCURRENT);    for (int i = 0; i < 20; ++i) {        [self loadPhotos:i];    }}

模拟下载照片并在完成后添加到数组

- (void)loadPhotos:(int)index {    dispatch_async(_photoQueue, ^{        [NSThread sleepForTimeInterval:1.0];        NSString *fileName = [NSString stringWithFormat:@"%02d.jpg", index % 10 + 1];        NSString *path = [[NSBundle mainBundle] pathForResource:fileName ofType:nil];        UIImage *image = [UIImage imageWithContentsOfFile:path];        [self.photoList addObject:image];        NSLog(@"添加照片 %@", fileName);    });}

运行测试


由于
NSMutableArray
是非线程安全的,如果出现两个线程在同一时间向数组中添加对象,会出现程序崩溃的情况

解决办法

NSLog(@"添加照片 %@", fileName);dispatch_barrier_async(_photoQueue, ^{    [self.photoList addObject:image];    NSLog(@"OK %@", [NSThread currentThread]);});

使用
dispatch_barrier_async
添加的 block 会在之前添加的 block 全部运行结束之后,才在同一个线程顺序执行,从而保证对非线程安全的对象进行正确的操作!


Barrier 工作示意图



注意:
dispatch_barrier_async
必须使用自定义队列,否则执行效果和全局队列一致


全局队列

是系统为了方便程序员开发提供的,其工作表现与
并发队列
一致

全局队列 & 并发队列的区别

全局队列没有名称
无论 MRC & ARC 都不需要考虑释放
日常开发中,建议使用全局队列

并发队列有名字,和
NSThread
name
属性作用类似
如果在 MRC 开发时,需要使用
dispatch_release(q);
释放相应的对象
dispatch_barrier
必须使用自定义的并发队列
开发第三方框架时,建议使用并发队列

全局队列 异步任务

/** 提问:是否开线程?是否顺序执行?come here 的位置? */- (void)gcdDemo8 {    // 1. 队列    dispatch_queue_t q = dispatch_get_global_queue(0, 0);    // 2. 执行任务    for (int i = 0; i < 10; ++i) {        dispatch_async(q, ^{            NSLog(@"%@ - %d", [NSThread currentThread], i);        });    }    NSLog(@"come here");}

运行效果与并发队列相同


参数

服务质量(队列对任务调度的优先级)/iOS 7.0 之前,是优先级

iOS 8.0(新增,暂时不能用,今年年底)
QOS_CLASS_USER_INTERACTIVE
0x21, 用户交互(希望最快完成-不能用太耗时的操作)
QOS_CLASS_USER_INITIATED
0x19, 用户期望(希望快,也不能太耗时)
QOS_CLASS_DEFAULT
0x15, 默认(用来底层重置队列使用的,不是给程序员用的)
QOS_CLASS_UTILITY
0x11, 实用工具(专门用来处理耗时操作!)
QOS_CLASS_BACKGROUND
0x09, 后台
QOS_CLASS_UNSPECIFIED
0x00, 未指定,可以和iOS 7.0 适配

iOS 7.0
DISPATCH_QUEUE_PRIORITY_HIGH
2 高优先级
DISPATCH_QUEUE_PRIORITY_DEFAULT
0 默认优先级
DISPATCH_QUEUE_PRIORITY_LOW
(-2) 低优先级
DISPATCH_QUEUE_PRIORITY_BACKGROUND
INT16_MIN 后台优先级

为未来保留使用的,应该永远传入0

结论:如果要适配 iOS 7.0 & 8.0,使用以下代码:
dispatch_get_global_queue(0, 0);


延迟操作


// MARK: - 延迟执行- (void)delay {    /**     从现在开始,经过多少纳秒,由"队列"调度异步执行 block 中的代码     参数     1. when    从现在开始,经过多少纳秒     2. queue   队列     3. block   异步执行的任务     */    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC));    void (^task)() = ^ {        NSLog(@"%@", [NSThread currentThread]);    };    // 主队列//    dispatch_after(when, dispatch_get_main_queue(), task);    // 全局队列//    dispatch_after(when, dispatch_get_global_queue(0, 0), task);    // 串行队列    dispatch_after(when, dispatch_queue_create("itheima", NULL), task);    NSLog(@"come here");}- (void)after {    [self.view performSelector:@selector(setBackgroundColor:) withObject:[UIColor orangeColor] afterDelay:1.0];    NSLog(@"come here");}

一次性执行

有的时候,在程序开发中,有些代码只想从程序启动就只执行一次,典型的应用场景就是“单例”


// MARK: 一次性执行- (void)once {    static dispatch_once_t onceToken;    NSLog(@"%ld", onceToken);    dispatch_once(&onceToken, ^{        [NSThread sleepForTimeInterval:1.0];        NSLog(@"一次性吗?");    });    NSLog(@"come here");}

dispatch 内部也有一把锁,是能够保证线程安全的!而且是苹果公司推荐使用的
以下代码用于测试多线程的一次性执行

- (void)demoOnce {    for (int i = 0; i < 10; ++i) {        dispatch_async(dispatch_get_global_queue(0, 0), ^{            [self once];        });    }}

单例测试

单例的特点

在内存中只有一个实例
提供一个全局的访问点

单例实现

// 使用 dispatch_once 实现单例+ (instancetype)sharedSingleton {    static id instance;    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        instance = [[self alloc] init];    });    return instance;}// 使用互斥锁实现单例+ (instancetype)sharedSync {    static id syncInstance;    @synchronized(self) {        if (syncInstance == nil) {            syncInstance = [[self alloc] init];        }    }    return syncInstance;}

面试时只要实现上面
sharedSingleton
方法即可


单例测试

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {    long largeNumber = 1000 * 1000;    // 测试互斥锁    CFAbsoluteTime start = CFAbsoluteTimeGetCurrent();    for (long i = 0; i < largeNumber; ++i) {        [Singleton sharedSync];    }    NSLog(@"互斥锁: %f", CFAbsoluteTimeGetCurrent() - start);    // 测试 dispatch_once    start = CFAbsoluteTimeGetCurrent();    for (long i = 0; i < largeNumber; ++i) {        [Singleton sharedSingleton];    }    NSLog(@"dispatch_once: %f", CFAbsoluteTimeGetCurrent() - start);}

调度组

常规用法

- (void)group1 {    // 1. 调度组    dispatch_group_t group = dispatch_group_create();    // 2. 队列    dispatch_queue_t q = dispatch_get_global_queue(0, 0);    // 3. 将任务添加到队列和调度组    dispatch_group_async(group, q, ^{        [NSThread sleepForTimeInterval:1.0];        NSLog(@"任务 1 %@", [NSThread currentThread]);    });    dispatch_group_async(group, q, ^{        NSLog(@"任务 2 %@", [NSThread currentThread]);    });    dispatch_group_async(group, q, ^{        NSLog(@"任务 3 %@", [NSThread currentThread]);    });    // 4. 监听所有任务完成    dispatch_group_notify(group, q, ^{        NSLog(@"OVER %@", [NSThread currentThread]);    });    // 5. 判断异步    NSLog(@"come here");}

enter & leavel

// MARK: - 调度组 2- (void)group2 {    // 1. 调度组    dispatch_group_t group = dispatch_group_create();    // 2. 队列    dispatch_queue_t q = dispatch_get_global_queue(0, 0);    // dispatch_group_enter & dispatch_group_leave 必须成对出现    dispatch_group_enter(group);    dispatch_group_async(group, q, ^{        NSLog(@"任务 1 %@", [NSThread currentThread]);        // dispatch_group_leave 必须是 block 的最后一句        dispatch_group_leave(group);    });    dispatch_group_enter(group);    dispatch_group_async(group, q, ^{        NSLog(@"任务 2 %@", [NSThread currentThread]);        // dispatch_group_leave 必须是 block 的最后一句        dispatch_group_leave(group);    });    // 4. 阻塞式等待调度组中所有任务执行完毕    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);    // 5. 判断异步    NSLog(@"OVER %@", [NSThread currentThread]);}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: