iOS多線程方案

iOS開發(fā)中需要將一些耗時操作放到子線程中去執(zhí)行, 防止阻塞主線程造成卡頓現(xiàn)象, 這時就用到了多線程.
本篇文章主要講解iOS中多線程的方案斑鸦、GCD的使用颂鸿、NSOperation的使用败晴、死鎖現(xiàn)象以及死鎖的本質、多線程的同步方案、文件的多讀單寫操作等.

一. 概念

iOS開發(fā)中, 最常用的兩種多線程方案就是GCDNSOperation了.

多線程方案 簡介 語言 生命周期
GCD 充分利用系統(tǒng)的多核 C 自動管理
NSOperation 對GCD的封裝, 更加面向對象, 增加了一下實用功能 OC 自動管理

在理解多線程之前需要對以下概念進行了解

  • 進程: 簡單理解為正在運行的一個App就是一個進程.
  • 線程: 線程用來執(zhí)行任務, 一個進程中可以有多個線程.
  • 同步執(zhí)行: 阻塞當前線程, 在當前線程中執(zhí)行任務, 不具備開啟線程的能力.
  • 異步執(zhí)行: 不阻塞當前線程, 具備開啟線程的能力(不一定不開啟新線程)
  • 串行隊列: 從隊列中取出任務的順序是FIFO, 上一個任務執(zhí)行完成之后再執(zhí)行下一個任務
  • 并發(fā)隊列: 從隊列中取出任務的順序是FIFO, 多個任務同時執(zhí)行, 任務完成的順序不確定(個人理解為, 從隊列中取任務分發(fā)到線程去執(zhí)行)
并發(fā)隊列 串行隊列 主隊列
同步執(zhí)行(sync) 沒有開啟線程, 串行執(zhí)行任務 沒有開啟線程, 串行執(zhí)行任務 沒有開啟線程, 串行執(zhí)行任務
異步執(zhí)行(async) 有開啟線程, 并發(fā)執(zhí)行任務 有開啟線程, 串行執(zhí)行任務 沒有開啟線程, 串行執(zhí)行任務

二. GCD

GCD中的隊列有四種:

    // 串行隊列
    dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_SERIAL);
    // 并發(fā)隊列
    dispatch_queue_t queue2 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_CONCURRENT);
    // 主隊列
    dispatch_queue_t queue3 = dispatch_get_main_queue();
    // 全局并發(fā)隊列
    dispatch_queue_t queue4 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);

在使用dispatch_queue_create創(chuàng)建隊列時, 第一個參數(shù)是隊列的字符串標簽(隊列的唯一標識), 格式為com.example.myqueue, 參數(shù)可選, 默認為NULL. 在iOS4.3以后第二個參數(shù)傳入DISPATCH_QUEUE_SERIAL用于創(chuàng)建串行隊列, DISPATCH_QUEUE_CONCURRENT用于創(chuàng)建并發(fā)隊列, 在iOS4.3之前必須傳入NULL.
使用dispatch_get_main_queue獲取和主線程相關聯(lián)的主隊列, 注意在主隊列中使用 dispatch_suspend, dispatch_resume, dispatch_set_context函數(shù), 無效果.
使用dispatch_get_global_queue獲取全局并發(fā)隊列, 第一個參數(shù)用于設置隊列的優(yōu)先級, 有下列四個值, 第二個參數(shù)傳入0.

#define DISPATCH_QUEUE_PRIORITY_HIGH 2
#define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
#define DISPATCH_QUEUE_PRIORITY_LOW (-2)
#define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN

2.1 同步執(zhí)行, 串行隊列

- (void)gcdTest1 {
    
    // 串行隊列
    dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_sync(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
        }
    });
    
}

同步執(zhí)行不具備開啟線程的能力, 串行隊列任務一個一個執(zhí)行.

2018-09-03 20:54:20.057889+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058138+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058289+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058442+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058588+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058740+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.058882+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.059559+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007be40>{number = 1, name = main}
2018-09-03 20:54:20.060899+0800 ThredDemo[23108:819649] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007be40>{number = 1, name = main}

2.2 異步執(zhí)行, 串行隊列

- (void)gcdTest2 {
    
    // 串行隊列
    dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_SERIAL);
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
        }
    });
    
}

異步執(zhí)行具備開啟線程的能力, 串行隊列任務會一個執(zhí)行完成再執(zhí)行另一個.

2018-09-03 20:55:09.989752+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.990425+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.990612+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.990847+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991174+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991261+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991349+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991428+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}
2018-09-03 20:55:09.991548+0800 ThredDemo[23127:820426] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400026ffc0>{number = 3, name = (null)}

2.3 同步執(zhí)行, 并發(fā)隊列

- (void)gcdTest3 {
    
    // 并發(fā)隊列    
    dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_sync(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_sync(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
        }
    });
    
}

同步執(zhí)行不具備開啟線程能力, 在當前線程中執(zhí)行.

2018-09-03 20:55:37.926704+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.926881+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.927068+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.927418+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.927712+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.928126+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.928506+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.929594+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007f180>{number = 1, name = main}
2018-09-03 20:55:37.930951+0800 ThredDemo[23154:821391] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x60400007f180>{number = 1, name = main}

2.4 異步執(zhí)行, 并發(fā)隊列

- (void)gcdTest4 {
    
    // 并發(fā)隊列
    dispatch_queue_t queue1 = dispatch_queue_create("com.onealon.gcdTest1", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務1-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務2-%@", [NSThread currentThread]);
        }
    });
    
    dispatch_async(queue1, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"同步執(zhí)行-串行隊列-任務3-%@", [NSThread currentThread]);
        }
    });
    
}

異步執(zhí)行具備開啟線程的能力, 并發(fā)執(zhí)行任務可以同時執(zhí)行多個.

2018-09-03 20:57:57.697746+0800 ThredDemo[23206:823843] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x604000261c40>{number = 7, name = (null)}
2018-09-03 20:57:57.697818+0800 ThredDemo[23206:823621] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x6000002733c0>{number = 6, name = (null)}
2018-09-03 20:57:57.697916+0800 ThredDemo[23206:823844] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x604000261fc0>{number = 8, name = (null)}
2018-09-03 20:57:57.699438+0800 ThredDemo[23206:823843] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x604000261c40>{number = 7, name = (null)}
2018-09-03 20:57:57.700144+0800 ThredDemo[23206:823621] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x6000002733c0>{number = 6, name = (null)}
2018-09-03 20:57:57.700292+0800 ThredDemo[23206:823844] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x604000261fc0>{number = 8, name = (null)}
2018-09-03 20:57:57.700618+0800 ThredDemo[23206:823843] 同步執(zhí)行-串行隊列-任務2-<NSThread: 0x604000261c40>{number = 7, name = (null)}
2018-09-03 20:57:57.700776+0800 ThredDemo[23206:823621] 同步執(zhí)行-串行隊列-任務1-<NSThread: 0x6000002733c0>{number = 6, name = (null)}
2018-09-03 20:57:57.700811+0800 ThredDemo[23206:823844] 同步執(zhí)行-串行隊列-任務3-<NSThread: 0x604000261fc0>{number = 8, name = (null)}

2.5 一次性函數(shù) dispatch_once

- (void)gcdTest5 {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"任務1--只執(zhí)行了一次哦");
    });
}

在進程的生命周期內, 任務1只會執(zhí)行一次, 并且在多線程訪問時, 一次性函數(shù)是安全的.
一次性函數(shù)dispatch_once經(jīng)常用于創(chuàng)建單例

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

之前在做App啟動時間優(yōu)化時, 需要將load中的代碼執(zhí)行時間向后推遲, 這時可以使用initializedispatch_once方法組合, 將load中的代碼推遲到需要使用的時候再調用.

+ (void)initialize {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 做一些初始化的操作.
    });
}

2.6 延遲函數(shù) dispatch_after

- (void)gcdTest6 {
    /*
     等到指定的時間將任務block異步添加到指定的隊列中.
     */
    NSLog(@"begin---");
    dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), queue, ^{
        NSLog(@"延遲函數(shù)--%@", [NSThread currentThread]);
    });
    NSLog(@"end---");
}

執(zhí)行結果:

2018-09-04 10:50:42.074319+0800 ThredDemo[3066:85334] begin---
2018-09-04 10:50:42.079407+0800 ThredDemo[3066:85334] end---
2018-09-04 10:50:44.278166+0800 ThredDemo[3066:85413] 延遲函數(shù)--<NSThread: 0x604000462640>{number = 3, name = (null)}

延時函數(shù)并非是在指定的時間后開始執(zhí)行任務block, 而是在指定的時間后將任務block異步添加到指定的隊列中, 然后再等待從隊列中取出任務block放入線程中去執(zhí)行, 如果block分配的線程被sleep了一段時間, 那么這個延遲函數(shù)就不太準確了, 所以延遲函數(shù)dispatch_after存在一定的時間誤差.

2.7 柵欄函數(shù) dispatch_barrier_async

- (void)gcdTest7 {
 
    NSLog(@"begin---");
    
    // 手動創(chuàng)建并發(fā)隊列
    dispatch_queue_t queue = dispatch_queue_create("com.onealon.queue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"耗時任務1--%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"耗時任務2--%@", [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"耗時任務1和任務2執(zhí)行完成以后--任務3--%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"任務4--%@", [NSThread currentThread]);
    });
    
    NSLog(@"end---");
}

執(zhí)行結果:

2018-09-04 10:55:25.360533+0800 ThredDemo[3148:88215] begin---
2018-09-04 10:55:25.365543+0800 ThredDemo[3148:88215] end---
2018-09-04 10:55:25.365730+0800 ThredDemo[3148:88889] 耗時任務2--<NSThread: 0x604000271640>{number = 4, name = (null)}
2018-09-04 10:55:25.365730+0800 ThredDemo[3148:88249] 耗時任務1--<NSThread: 0x600000073bc0>{number = 3, name = (null)}
2018-09-04 10:55:25.366010+0800 ThredDemo[3148:88249] 耗時任務1和任務2執(zhí)行完成以后--任務3--<NSThread: 0x600000073bc0>{number = 3, name = (null)}
2018-09-04 10:55:25.367820+0800 ThredDemo[3148:88249] 任務4--<NSThread: 0x600000073bc0>{number = 3, name = (null)}

當柵欄函數(shù)中的任務block到達并發(fā)隊列queue的最前邊時, 任務block并不會立即執(zhí)行, 它會等待并發(fā)隊列中正在執(zhí)行的任務執(zhí)行完成以后才執(zhí)行柵欄函數(shù)的任務. 任何在柵欄函數(shù)以后添加到并發(fā)隊列中的任務都會等待柵欄函數(shù)中的任務執(zhí)行完成以后才執(zhí)行.

使用dispatch_barrier_async需要注意:

  • 柵欄函數(shù)中攔截的任務必須和耗時任務在同一個隊列中.
  • 柵欄函數(shù)中的隊列必須是使用dispatch_queue_create手動創(chuàng)建的并發(fā)隊列
    如果是串行隊列或者全局并發(fā)隊列, 那么柵欄函數(shù)就相當于dispatch_async函數(shù), 不會起到攔截效果.

2.8 隊列組

- (void)gcdTest8 {
    
    NSLog(@"begin---");
    
    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務1-%@",[NSThread currentThread]);
        }
    });

    dispatch_group_async(group, queue, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務2-%@",[NSThread currentThread]);
        }
    });
    
    dispatch_group_notify(group, queue2, ^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務3-%@",[NSThread currentThread]);
        }
    });
    
    NSLog(@"end---");
}

執(zhí)行結果:

2018-09-04 11:12:34.087597+0800 ThredDemo[3435:99939] begin---
2018-09-04 11:12:34.088345+0800 ThredDemo[3435:99939] end---
2018-09-04 11:12:34.088427+0800 ThredDemo[3435:99995] 任務1-<NSThread: 0x60400046e100>{number = 3, name = (null)}
2018-09-04 11:12:34.088427+0800 ThredDemo[3435:100452] 任務2-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.090944+0800 ThredDemo[3435:100452] 任務2-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.090944+0800 ThredDemo[3435:99995] 任務1-<NSThread: 0x60400046e100>{number = 3, name = (null)}
2018-09-04 11:12:34.091614+0800 ThredDemo[3435:99995] 任務1-<NSThread: 0x60400046e100>{number = 3, name = (null)}
2018-09-04 11:12:34.091634+0800 ThredDemo[3435:100452] 任務2-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.091777+0800 ThredDemo[3435:100452] 任務3-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.104841+0800 ThredDemo[3435:100452] 任務3-<NSThread: 0x600000266240>{number = 4, name = (null)}
2018-09-04 11:12:34.107901+0800 ThredDemo[3435:100452] 任務3-<NSThread: 0x600000266240>{number = 4, name = (null)}

dispatch_group_notify會監(jiān)聽隊列組group中的任務, 當隊列組中的任務執(zhí)行完成以后, 會將dispatch_group_notify中的任務添加到指定隊列中.
如果隊列組中沒有任何關聯(lián)的任務, dispatch_group_notify會立即將任務添加到指定隊列.

dispatch_group_notifydispatch_barrier_async的區(qū)別:

  • dispatch_barrier_async只能攔截在同一個隊列中的任務
  • dispatch_group_notify可以監(jiān)聽不同隊列中的任務

2.9 GCD中隊列的區(qū)別

主隊列:

  • 和主線程相關聯(lián)的隊列
  • 不能調用dispatch_suspend, dispatch_resume, dispatch_set_context方法
  • 當gcd代碼在主隊列執(zhí)行時, 會將block中的代碼添加到runloop中, runloop調用__CFRUNLOOP_IS_SERVICING_THE_MAIN_DISPATCH_QUEUE__執(zhí)行block代碼(這部分還需要在研究一下)

全局并發(fā)隊列:

  • 全局并發(fā)隊列整個進程共享, 有高 中 低 后臺 四個優(yōu)先級
  • 手動創(chuàng)建的隊列可以取消, 全局隊列不能取消(Calls to the dispatch_suspend, dispatch_resume, and dispatch_set_context functions have no effect on the returned queues.)
  • 全局并發(fā)隊列不能使用在柵欄函數(shù)dispatch_barrier_async

三. NSOperation

NSOperation的使用步驟:

  1. 使用NSOperation的子類創(chuàng)建操作operation
  2. 使用NSOperationQueue創(chuàng)建隊列queue
  3. 將操作operation添加到隊列queue中, 系統(tǒng)會自動去除隊列中的任務執(zhí)行.

NSOperation是一個抽象類, 在使用的時候可以使用它的子類NSBlockOperationNSInvocationOperation, 或者創(chuàng)建NSOperation的子類.
NSOperationQueue分兩種隊列, 一種是通過[NSOperationQueue mainQueue]獲取的主隊列(主隊列是串行隊列), 另一種是通過[[NSOperationQueue alloc] init]手動創(chuàng)建的隊列. 手動創(chuàng)建的隊列可以通過設置maxConcurrentOperationCount屬性控制器隊列是串行還是并發(fā), maxConcurrentOperationCount=1是串行隊列.

3.1 NSBlockOperation

- (void)operationTest1 {
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務1-%@",[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務2-%@",[NSThread currentThread]);
        }
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 1;
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

3.2 NSInvocationOperation

- (void)operationTest2 {
    
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation1Selector) object:nil];
    
    
    NSInvocationOperation *operation2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(operation2Selector) object:nil];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

- (void)operation1Selector {
    for (int i = 0; i < 3; i++) {
        NSLog(@"任務1-%@",[NSThread currentThread]);
    }
}

- (void)operation2Selector {
    for (int i = 0; i < 3; i++) {
        NSLog(@"任務2-%@",[NSThread currentThread]);
    }
}

3.3 匿名操作

- (void)operationTest3 {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務1-%@",[NSThread currentThread]);
        }
    }];
    
    [queue addOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務2-%@",[NSThread currentThread]);
        }
    }];

}

3.4 取消 暫停 恢復操作

- (void)operationTest5 {
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務1-%@",[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務2-%@",[NSThread currentThread]);
        }
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 取消某一個操作
//    [operation1 cancel];
    
    // 暫停隊列
    [queue setSuspended:YES];

    // 取消隊列中的操作
//    [queue cancelAllOperations];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.f * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [queue setSuspended:NO];
    });
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    
}

3.5 設置依賴

- (void)operationTest4 {
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務1-%@",[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務2-%@",[NSThread currentThread]);
        }
    }];
    
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        for (int i = 0; i < 3; i++) {
            NSLog(@"任務3-%@",[NSThread currentThread]);
        }
    }];
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [operation2 addDependency:operation3];
    
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
    
}

3.6 線程間通訊

- (void)operationTest6 {
    
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    [queue addOperationWithBlock:^{
        NSLog(@"在子線程中執(zhí)行--任務1--%@", [NSThread currentThread]);
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
                NSLog(@"在主線程中執(zhí)行--任務2--%@", [NSThread currentThread]);
        }];
    }];
    
}

四. 線程死鎖問題

在使用多線程的時候要特別注意線程死鎖的問題, 以下代碼均在主線程執(zhí)行, 會死鎖嗎?

- (void)interview1 {
    NSLog(@"執(zhí)行任務1");
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"執(zhí)行任務2");
    });
    NSLog(@"執(zhí)行任務3");
}

會造成線程死鎖的現(xiàn)象
gcd代碼同步執(zhí)行, 會阻塞當前線程, 也就是任務2執(zhí)行完成以后才會執(zhí)行任務3. 主隊列是串行隊列, 當一個任務執(zhí)行完成以后才會執(zhí)行另一個任務, 也就是任務3執(zhí)行完成以后才會執(zhí)行任務2. 所以任務2和任務3就會一直相互等待, 形成死鎖.

- (void)interview2 {
    NSLog(@"執(zhí)行任務1");
    dispatch_async(dispatch_get_main_queue(), ^{
        NSLog(@"執(zhí)行任務2");
    });
    NSLog(@"執(zhí)行任務3");
}

不會造成死鎖
gcd代碼異步執(zhí)行, 不會阻塞當前線程, 個人理解為gcd代碼會將任務2添加到主隊列中, 而interview2也是在主隊列中, 主隊列中的任務執(zhí)行順序是一個一個執(zhí)行, 所以, 執(zhí)行順序是1-3-2

- (void)interview3
{
    NSLog(@"執(zhí)行任務1");
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"執(zhí)行任務2");
        dispatch_sync(queue, ^{
            NSLog(@"執(zhí)行任務3");
        });
        NSLog(@"執(zhí)行任務4");
    });
    NSLog(@"執(zhí)行任務5");
}

任務2不會死鎖, 任務3會死鎖.
queue是串行隊列, 執(zhí)行任務是一個一個執(zhí)行, async代碼是異步執(zhí)行, 會開啟新線程, 任務2是在子線程中執(zhí)行, sync代碼是同步執(zhí)行, 不會開啟新線程, 阻塞當前線程, 任務3會等待隊列中的async任務執(zhí)行完成以后, 再取出sync代碼執(zhí)行, 而sync代碼已經(jīng)阻塞了當前線程, 所以造成死鎖.

- (void)interview4
{
    NSLog(@"執(zhí)行任務1");
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_SERIAL);
    dispatch_queue_t queue2 = dispatch_queue_create("myqueu2", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"執(zhí)行任務2");
        dispatch_sync(queue2, ^{
            NSLog(@"執(zhí)行任務3");
        });
        NSLog(@"執(zhí)行任務4");
    });
    NSLog(@"執(zhí)行任務5");
}

不會造成死鎖
queue和queue2雖然都是串行隊列, async+queue會開啟新線程, sync雖然阻塞當前線程, 但是sync的任務是從queue2隊列中獲取, 不會造成死鎖

- (void)interview5
{
    NSLog(@"執(zhí)行任務1");
    dispatch_queue_t queue = dispatch_queue_create("myqueu", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"執(zhí)行任務2");
        dispatch_sync(queue, ^{
            NSLog(@"執(zhí)行任務3");
        });
        NSLog(@"執(zhí)行任務4");
    });
    NSLog(@"執(zhí)行任務5");
}

不會造成死鎖
queue是并發(fā)隊列, 并發(fā)隊列可以同時執(zhí)行多個任務, async+queue會開啟新線程, 在子線程執(zhí)行async代碼, sync雖然會阻塞當前線程, 但是queue是并發(fā)隊列, 所以執(zhí)行任務3時不必等待async代碼從隊列中執(zhí)行完成.

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市柱衔,隨后出現(xiàn)的幾起案子奔滑,更是在濱河造成了極大的恐慌朋其,老刑警劉巖叼耙,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件癞松,死亡現(xiàn)場離奇詭異硕勿,居然都是意外死亡扼褪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來赏寇,“玉大人,你說我怎么就攤上這事椒功《∈海” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我尘分,道長,這世上最難降的妖魔是什么谍咆? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任庇茫,我火速辦了婚禮查坪,結果婚禮上偿曙,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好歉备,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布帽驯。 她就那樣靜靜地躺著,像睡著了一般尼变。 火紅的嫁衣襯著肌膚如雪吸申。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天享甸,我揣著相機與錄音,去河邊找鬼蛉威。 笑死日丹,一個胖子當著我的面吹牛,可吹牛的內容都是我干的蚯嫌。 我是一名探鬼主播哲虾,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼丙躏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了束凑?” 一聲冷哼從身側響起晒旅,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎汪诉,沒想到半個月后废恋,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡扒寄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年鱼鼓,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片该编。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡迄本,死狀恐怖,靈堂內的尸體忽然破棺而出课竣,到底是詐尸還是另有隱情嘉赎,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布于樟,位于F島的核電站公条,受9級特大地震影響,放射性物質發(fā)生泄漏隔披。R本人自食惡果不足惜赃份,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一寂拆、第九天 我趴在偏房一處隱蔽的房頂上張望奢米。 院中可真熱鬧,春花似錦纠永、人聲如沸鬓长。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽涉波。三九已至,卻和暖如春炭序,著一層夾襖步出監(jiān)牢的瞬間啤覆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工惭聂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留窗声,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓辜纲,卻偏偏與公主長得像笨觅,于是被迫代替她去往敵國和親拦耐。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內容