ios - 多線程之六:GCD串行

在之前的項目技術上,我們進行學習

1:創建入口按鈕

//第三種方式 GCD
UIButton *btn2 = [UIButton buttonWithType:UIButtonTypeCustom];
btn2.frame = CGRectMake(40, 250, 100, 40);
[btn2 setTitle:@"GCD串行" forState:UIControlStateNormal];
[btn2 setBackgroundColor:[UIColor blueColor]];
[btn2 addTarget:self action:@selector(click_GCD_serial) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn2];

2:在 click_GCD_serial 方法中測試

dispatch_get_main_queue

情況一 : 在主隊列中開啓異步線程

dispatch_async(dispatch_get_main_queue(), ^(){
    /在主線程隊列中開啓異步任務
    NSLog(@"開始子線程 :task1");
    for (int i = 10; i <= 20 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    }

});

dispatch_async(dispatch_get_main_queue(), ^(){
    /在主線程隊列中開啓異步任務
    NSLog(@"開始子線程 :task2");
    for (int i = 20; i <= 30 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 30) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程,task2");
            });
        }
    }


});

dispatch_async(dispatch_get_main_queue(), ^(){
    /在主線程隊列中開啓異步任務
    NSLog(@"開始子線程 :task3");
    for (int i = 30; i <= 40 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 40) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task3");
            });
        }
    }

});

執行結果:

主隊列+異步

根據結果我們分析出:
1:在主隊列中執行異步任務還是會堵塞主線程,
2:系統不會開啓新的線程來執行異步任務;
3:所有的異步任務都會在主線程中執行;
4:所有的異步任務按照順序依次執行;
5:充分證明主隊列是串行隊列;

情況一 : 在主隊列中開啓同步線程
細心的朋友可能發現了,爲什麼要使用異步任務和主隊列進行組合呢,如果是同步任務和主隊列會是什麼樣的結果呢?我們用代碼進行求證;

 dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"回到主線程");
 });

執行結果:

主線程 error

程序直接crash。思考一下爲什麼?

總結:
在主隊列中開啓任務,任務只能是異步任務,否則系統就會崩潰。也就是說不能使用創建同步任務調用主隊列。
錯誤代碼:

 //錯誤例子如下:
 dispatch_sync(dispatch_get_main_queue(), ^{
    NSLog(@"回到主線程");
 });

應用自創建串行隊列

場景應用: 要求線程中的任務按照順序來執行,或者線程之前之前存在依賴關係。

情況一:如果隊列中的任務都是同步任務

 dispatch_queue_t serial = dispatch_queue_create("queue.costom", NULL); //創建串行隊列
dispatch_sync(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task4");
    for (int i = 40; i <= 50 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 50) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task4");
            });
        }
    }
});

dispatch_sync(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task5");
    for (int i = 50; i <= 60 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 60) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task5");
            });
        }
    }
});

dispatch_sync(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task6");
    for (int i = 60; i <= 70 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 70) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task6");
            });
        }
    }
});

執行結果:

串行 + 同步
結果分析:
1:會堵塞主線程;
2:不會開啓新線程;
3:在主線程中執行任務,會堵塞主線程;
4:任務會按照順序依次執行;

情況二 : 如果隊列中的任務都是異步任務

dispatch_queue_t serial = dispatch_queue_create("queue.costom", NULL); //創建串行隊列
dispatch_async(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task4");
    for (int i = 40; i <= 50 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 50) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task4");
            });
        }
    }
});

dispatch_async(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task5");
    for (int i = 50; i <= 60 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 60) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task5");
            });
        }
    }
});

dispatch_async(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task6");
    for (int i = 60; i <= 70 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
        if (i == 70) {
            dispatch_async(dispatch_get_main_queue(), ^{
                NSLog(@"回到主線程, task6");
            });
        }
    }
});
執行結果:

串行 + 異步
日誌分析:
1:不會堵塞主線程
2:會開啓一個子線程,所有的異步任務都在該線程內執行;
3:在子線程中的任務會按照順序依次執行

情況三:如果隊列中的任務有同步任務和異步任務

 dispatch_queue_t serial = dispatch_queue_create("queue.costom", NULL); //創建串行隊列
dispatch_async(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task4");
    for (int i = 40; i <= 50 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
//            if (i == 50) {
//                dispatch_async(dispatch_get_main_queue(), ^{
//                    NSLog(@"回到主線程, task4");
//                });
//            }
    }
});

    dispatch_sync(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task5");
    for (int i = 50; i <= 60 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
  //            if (i == 60) {
  //                dispatch_async(dispatch_get_main_queue(), ^{
  //                    NSLog(@"回到主線程, task5");
  //                });
  //            }
    }
});

dispatch_async(serial, ^{
    //在主線程隊列中開啓同步任務
    NSLog(@"開始子線程 :task6");
    for (int i = 60; i <= 70 ; i ++) {
        sleep(1);
        NSLog(@"當前線程名稱:%@ ——%d",[NSThread currentThread].name,i);
    //            if (i == 70) {
    //                dispatch_async(dispatch_get_main_queue(), ^{
    //                    NSLog(@"回到主線程, task6");
    //                });
    //            }
    }
});

執行結果:

串行 + 同步 + 異步

日誌分析:
1:同步還是會在主線程中執行,造成主線程堵塞
2:異步任務會開啓子線程,並且異步任務在子線程中執行,此時不會造成主線程的堵塞
3:無論是同步任務或者異步任務,所有任務都是按照順序執行

至此,對串行隊列的學習和演示結束,我們可以得出以下的總結:
1:只要是串行隊列,無論任務是同步或者異步,所有的任務都是要按照順序依次執行的;
2:如果任務是同步任務就會在主線程中直接依次執行, 堵塞主線程
3:如果任務是異步任務就會開闢子線程執行,但還是會按照順序依次執行,執行完成一個任務之後再執行下一項任務,不會堵塞主線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章