iOS 多线程编程 GCD

iOS May 23, 2015

这个话题有点沉重啊,多线程开发是个坑,这次还非跳不可,先喝口茶整理整理思绪 ⊙▽⊙

先说下 GCD 吧,全名是 Grand Central Dispatch,而不是某执政部门,直译成中文是 雄壮的中央调度机制,光听名字就知道这货是个高级玩意儿,不过我水平也有限,有没有提及到的地方,还希望多多交流。

  • GCD 的基本思想是就将操作放在队列中去执行
  • 操作使用 Block 定义
  • 队列负责调度任务执行所在的线程以及具体的执行时间
  • 队列的特点是先进先出(FIFO)的,新添加至对列的操作都会排在队尾
  • GCD 的函数都是以 dispatch 开头的

自定义队列

通常是 create 一个 dispatch_queue_t 队列,然后把任务放到队列中,队列分为 dispatch_async (异步队列)和 dispatch_sync (同步队列)。

串行队列

- (void)gcdDemo1 {
    
    // 串行队列
    dispatch_queue_t q = dispatch_queue_create("com.pupboss.gcddemo1", DISPATCH_QUEUE_SERIAL);
    
    // 串行行队列的同步任务,同样会在主线程上运行
    for (int i = 0; i < 5; ++i) {
        // 同步任务顺序执行
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    for (int i = 0; i < 5; ++i) {
        // 异步任务,并发执行,但是如果在串行队列中,仍然会依次顺序执行
        dispatch_async(q, ^{
            // [NSThread currentThread] 可以在开发中,跟踪当前线程
            // num = 1,表示主线程
            // num = 2,表示第2个子线程。。。
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}

2015-05-23 22:03:17.990 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 0
2015-05-23 22:03:17.990 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 1
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 2
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 3
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211585] <NSThread: 0x7feeb3f289d0>{number = 1, name = main} 4
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 0
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 1
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 2
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 3
2015-05-23 22:03:17.991 Multi-Thread-Test[4502:211685] <NSThread: 0x7feeb506dc70>{number = 2, name = (null)} 4

并行队列

这个比较有意思了,看下面这种情况

- (void)gcdDemo2 {
    // 并行队列,执行顺序不确定,线程个数不确定
    dispatch_queue_t q = dispatch_queue_create("com.pupboss.gcddemo2", DISPATCH_QUEUE_CONCURRENT);
    
    for (int i = 0; i < 5; ++i) {
        // 异步任务
        dispatch_async(q, ^{
            // [NSThread currentThread] 可以在开发中,跟踪当前线程
            // num = 1,表示主线程
            // num = 2,表示第2个子线程。。。
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
    
    for (int i = 0; i < 5; ++i) {
        // 同步任务顺序执行
        dispatch_sync(q, ^{
            NSLog(@"%@ %d", [NSThread currentThread], i);
        });
    }
}

执行结果是下面的,非常混乱,是吧,但是

2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212722] <NSThread: 0x7f98e9600000>{number = 5, name = (null)} 3
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212715] <NSThread: 0x7f98e9979e90>{number = 2, name = (null)} 0
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 0
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212714] <NSThread: 0x7f98e9a00a70>{number = 4, name = (null)} 1
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212716] <NSThread: 0x7f98e98301d0>{number = 3, name = (null)} 2
2015-05-23 22:04:03.804 Multi-Thread-Test[4527:212723] <NSThread: 0x7f98e9a003b0>{number = 6, name = (null)} 4
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 1
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 2
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 3
2015-05-23 22:04:03.805 Multi-Thread-Test[4527:212664] <NSThread: 0x7f98e9428a30>{number = 1, name = main} 4

如果把两个任务换换位置,变成这样

for (int i = 0; i < 5; ++i) {
    // 同步任务顺序执行
    dispatch_sync(q, ^{
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}

for (int i = 0; i < 5; ++i) {
    // 异步任务
    dispatch_async(q, ^{
        // [NSThread currentThread] 可以在开发中,跟踪当前线程
        // num = 1,表示主线程
        // num = 2,表示第2个子线程。。。
        NSLog(@"%@ %d", [NSThread currentThread], i);
    });
}

运行结果就变了

2015-05-23 22:04:58.874 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 0
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 1
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 2
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 3
2015-05-23 22:04:58.876 Multi-Thread-Test[4555:214018] <NSThread: 0x7fc8d3c28bd0>{number = 1, name = main} 4
2015-05-23 22:04:58.880 Multi-Thread-Test[4555:214043] <NSThread: 0x7fc8d3e00ae0>{number = 2, name = (null)} 0
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214043] <NSThread: 0x7fc8d3e00ae0>{number = 2, name = (null)} 1
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214044] <NSThread: 0x7fc8d3e03280>{number = 3, name = (null)} 2
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214043] <NSThread: 0x7fc8d3e00ae0>{number = 2, name = (null)} 4
2015-05-23 22:04:58.881 Multi-Thread-Test[4555:214046] <NSThread: 0x7fc8d3f0adb0>{number = 4, name = (null)} 3

说明并行队列中的同步任务,还是在主线程上,并且执行完了之后才会往下走

全局队列

苹果为了方便多线程的设计,提供一个全局队列,供所有的APP共同使用

dispatch_queue_t  queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

两个参数,一个是优先级,一个是 0,现在只能写 0,因为苹果没想好这个参数怎么开放给开发者。

全局队列用起来跟并行队列是差不多的,所以就不上代码了

主线程队列

每一个应用程序都只有一个主线程,有些操作,需要放到主线程队列来完成,比如数据下载完,刷新界面,等等

主线程队列千万不要使用同步任务,会发生阻塞!

- (void)gcdDemo4 {
    
    dispatch_queue_t q = dispatch_get_main_queue();
    
    // 异步任务,在主线程上运行,同时是保持队形的
    for (int i = 0; i < 5; ++i) {
        dispatch_async(q, ^{
            NSLog(@"%@ - %d", [NSThread currentThread], i);
        });
    }
}

运行结果

2015-05-23 22:34:17.663 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 0
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 1
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 2
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 3
2015-05-23 22:34:17.664 Multi-Thread-Test[4800:243581] <NSThread: 0x7fbffc023060>{number = 1, name = main} - 4

批量下载图片

for (int i = 1; i < 100; ++i) {
    
    dispatch_sync(q, ^{
        
        NSLog(@"%@ %d", [NSThread currentThread], i);
        NSString *imageName = [NSString stringWithFormat:@"%d.jpg", i];
        
        NSString *url = [NSString stringWithFormat:@"http://xxxxy/%@", imageName];
        
        [LJFileTool writeImageToFileName:imageName withImgURL:url];
    });
}

这么写没什么好处 = =,也就是不阻塞主线程,除此之外真没啥了。。。。卧槽,本来想写成异步任务的,结果发现老是有问题,而且速度也一般。

iOS 的推送

NSArray *startArr = [NSArray arrayWithContentsOfFile:@"/xxxxx/test.plist"];
    
    dispatch_queue_t q = dispatch_queue_create("com.pupboss.pusher", DISPATCH_QUEUE_SERIAL);
    
    for (int i = 0; i < startArr.count; ++i) {
        dispatch_async(q, ^{
            
            NSLog(@"%@ %d", [NSThread currentThread], i);
            
            NSLog(@"%@", startArr[i]);
            
            [self sendMsg:startArr[i]];
        });
    }

iOS 多线程编程 NSThread & NSOperation

原本打算一篇写完多线程的,但是实在太长了,不太合适,就又开了一篇。

NSThread 和 NSOperation 的可以点击 iOS 多线程编程 NSThread & NSOperation

Tags

Jie Li

🚘 On-road / 📉 US Stock / 💻 Full Stack Developer / 🎓 Grad Student / ®️ ENTJ

Great! You've successfully subscribed.
Great! Next, complete checkout for full access.
Welcome back! You've successfully signed in.
Success! Your account is fully activated, you now have access to all content.