Table of Content

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

先说下 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