iOS線程、同步異步、串行並行隊列

線程與隊列說不清道不明的關係:html

線程是代碼執行的路徑,隊列則是用於保存以及管理任務的,線程負責去隊列中取任務進行執行。 個人理解:多個隊列的任務能夠在一條線程上執行,一個隊列的任務也能夠在多條線程上執行。我的理解,隊列能夠包含線程,線程也能夠包含隊列。 ios

dispatch_sync:立馬在當前線程執行任務,執行完再往下走,這句話就能夠解釋不少問題。程序員

dispatch_async:不要求立馬在當前線程執行任務,可能會開啓新線程,也有可能不會。安全

1、畫圖解釋下隊列跟線程間的關係

一、一個隊列對應一個線程

"主隊列" 對應 "主線程"bash

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor blueColor];
    [self threadTest];
}
- (void)threadTest{
    NSLog(@"我是任務1");
    NSLog(@"我是任務2");
}
複製代碼

二、一個隊列對應兩個線程

"隊列1" 對應 "主線程"和"新線程" (由於主隊列沒有開啓新線程的能力因此用"隊列1")數據結構

- (void)threadTest{
    dispatch_queue_t queue = dispatch_queue_create("隊列1", DISPATCH_QUEUE_SERIAL);
    //同步 不開啓新線程,因此在主線程執行
    dispatch_sync(queue, ^{
        NSLog(@"任務1");
        [self getCurrentThread];
    });
    //異步 開啓新線程
    dispatch_async(queue, ^{
        NSLog(@"任務2");//2️⃣
        [self getCurrentThread];
    });
    sleep(3);//若是沒有這個方法,2️⃣3️⃣的執行前後順序是不肯定的,由於是兩個線程,執行前後沒有一毛錢關係
    NSLog(@"方法執行結束");//3️⃣
}
- (void)getCurrentThread{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"currentThread == %@",currentThread);
}
複製代碼
2018-06-20 14:40:05.904914+0800 WLZCyclycReference[4497:158751] 任務1
2018-06-20 14:40:05.905099+0800 WLZCyclycReference[4497:158751] currentThread == <NSThread: 0x60800007c000>{number = 1, name = main}
2018-06-20 14:40:05.905230+0800 WLZCyclycReference[4497:158829] 任務2
2018-06-20 14:40:05.905381+0800 WLZCyclycReference[4497:158829] currentThread == <NSThread: 0x608000270800>{number = 3, name = (null)}
2018-06-20 14:40:08.906308+0800 WLZCyclycReference[4497:158751] 方法執行結束
複製代碼

三、兩個隊列對應一個線程

"主隊列"和"隊列1" 對應 "主線程"多線程

- (void)threadTest{
    dispatch_queue_t queue = dispatch_queue_create("隊列1", DISPATCH_QUEUE_SERIAL);
    NSLog(@"我是任務2");
    [self getCurrentThread];
    //同步 不開啓新線程,因此在主線程執行
    dispatch_sync(queue, ^{
        sleep(3);//睡三秒能夠解釋不少問題
        NSLog(@"我是任務1");
        [self getCurrentThread];
    });
    NSLog(@"方法執行結束");
}
- (void)getCurrentThread{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"currentThread == %@",currentThread);
}
複製代碼
2018-06-20 14:53:39.210769+0800 WLZCyclycReference[4741:169494] 我是任務2
2018-06-20 14:53:39.210959+0800 WLZCyclycReference[4741:169494] currentThread == <NSThread: 0x608000065640>{number = 1, name = main}
2018-06-20 14:53:42.212170+0800 WLZCyclycReference[4741:169494] 我是任務1
2018-06-20 14:53:42.212615+0800 WLZCyclycReference[4741:169494] currentThread == <NSThread: 0x608000065640>{number = 1, name = main}
2018-06-20 14:53:42.212810+0800 WLZCyclycReference[4741:169494] 方法執行結束
複製代碼

四、兩個隊列對應兩個線程

"主隊列"和"隊列1" 對應 "主線程"和"新線程"併發

- (void)threadTest{
    dispatch_queue_t queue = dispatch_queue_create("隊列1", DISPATCH_QUEUE_SERIAL);
    NSLog(@"我是任務2");
    [self getCurrentThread];
    //開啓新線程
    dispatch_async(queue, ^{
        NSLog(@"我是任務1");//1️⃣
        [self getCurrentThread];
    });
    NSLog(@"方法執行結束");//2️⃣
    //1️⃣2️⃣的執行前後順序是隨機的
}
- (void)getCurrentThread{
    NSThread *currentThread = [NSThread currentThread];
    NSLog(@"currentThread == %@",currentThread);
}
複製代碼
2018-06-20 15:11:12.132538+0800 WLZCyclycReference[5076:184244] 我是任務2
2018-06-20 15:11:12.132705+0800 WLZCyclycReference[5076:184244] currentThread == <NSThread: 0x60400007bd80>{number = 1, name = main}
2018-06-20 15:11:12.132809+0800 WLZCyclycReference[5076:184244] 方法執行結束
2018-06-20 15:11:12.132823+0800 WLZCyclycReference[5076:184332] 我是任務1
2018-06-20 15:11:12.133009+0800 WLZCyclycReference[5076:184332] currentThread == <NSThread: 0x6080002602c0>{number = 3, name = (null)}
複製代碼

2、主線程特色講解

主線程特色:若是主線程裏有任務就必須等主線程任務執行完才輪到主隊列(若是是其餘隊列的任務,那麼任務就不用等待,會直接被主線程執行)的。因此說若是在主隊列異步(開啓新線程)執行任務無所謂,可是若是在主隊列同步(不開啓新線程,須要在主線程執行)執行任務會循環等待,形成死鎖(可是在通常串行隊列這樣執行就不會出問題,一切都是由於主線程的這個特色)。app

一、主線程特色演示

dispatch_queue_t mainQueue = dispatch_get_main_queue();
    dispatch_async(mainQueue, ^{
        NSLog(@"1");
        NSThread *mainThread = [NSThread currentThread];
        NSLog(@"currentThread == %@",mainQueue);
        sleep(3);
        NSLog(@"2");
    });
NSLog(@"主線程執行完了");
複製代碼
2018-06-20 12:09:05.598367+0800 WLZCyclycReference[3254:101260] 主線程執行完了
2018-06-20 12:09:05.601950+0800 WLZCyclycReference[3254:101260] 1
2018-06-20 12:09:05.602095+0800 WLZCyclycReference[3254:101260] currentThread == <OS_dispatch_queue_main: com.apple.main-thread>
2018-06-20 12:09:08.603048+0800 WLZCyclycReference[3254:101260] 2
複製代碼

從執行結果能夠完美的看出主線程的特色,因此我嚴重懷疑:主隊列的任務只能在主線程上執行,換句話說就是主隊列是串行隊列的閹割版,即主隊列沒有建立新線程的能力。異步

主線程在執行主隊列任務的過程當中:

  • 若是主隊列經過異步的方式添加"任務1",那麼既然主隊列沒有開新線程的能力,就等我主線程把我當前的任務執行完了,我主線程再去執行"任務1";
  • 若是主隊列經過同步的方式添加"任務2",那麼我主線程仍是要先執行完我當前的任務,可是"任務2"是同步的,"任務2"又必需要主線程執行本身。那主線程只好循環等待而後死了。

二、主線程死鎖演示,主線程產生死鎖針對的是主隊列

//主隊列  同步執行任務  死鎖
dispatch_sync(dispatch_get_main_queue(), ^{
});
複製代碼

三、主線程中,一樣的任務放到其它隊列(串行、並行都可)同步執行,不會死鎖,而且順序執行,執行完「串行隊列1」的任務,繼續向下執行主隊列的任務。

dispatch_queue_t queue = dispatch_queue_create("串行隊列1", DISPATCH_QUEUE_SERIAL);
//串行隊列  同步執行任務 由於沒有開啓線程因此仍是主線程
dispatch_sync(queue, ^{
});
複製代碼

上述代碼中"主隊列"、"串行隊列1"兩個隊列的任務互不干擾,這種狀況其實就至關於把兩個隊列放到了一個更大的虛擬並行隊列中,能夠同時執行任務

FIFO,串行隊列任務1沒有執行完,同步執行任務2的話絕逼死鎖,可是死鎖產生的緣由是同一個隊列裏邊的兩個任務相互等待,若是不是同一個隊列,那麼久不會產生死鎖。

四、同一個串行隊列,同步執行任務嵌套,形成死鎖。

dispatch_queue_t queue = dispatch_queue_create("串行隊列", DISPATCH_QUEUE_SERIAL);
//串行隊列  同步執行任務 由於沒有開啓線程因此仍是主線程
dispatch_sync(queue, ^{
    NSLog(@"1");
    dispatch_sync(queue, ^{
        NSLog(@"串行隊列嵌套執行");
    });
    NSLog(@"2");
});
複製代碼

上邊死鎖的狀況能夠說明一個問題,某一個串行隊列正在執行一個同步任務,這時候又在當前隊列插入了一個同步任務就會形成死鎖。這種狀況就跟1(主隊列有任務,主線程執行ing,主隊列插入同步任務形成死鎖)的狀況是同樣的,只不過主隊列是系統自帶的,咱們這個隊列是本身建立的而已。

同步異步:指的是函數(方法),可否開啓新的線程。同步不能,異步能夠

串行並行:指的是隊列,任務的執行方式,串行指各個任務按順序執行。並行指多個任務能夠同時執行

串行隊列也能夠開啓新的線程,只不過開啓以後也只是按順序執行,

3、概念解釋

如下概念參考"08號瘋子"的博客: IOS多線程知識總結/隊列概念/GCD/串行/並行/同步/異步

  • 進程:正在進行中的程序被稱爲進程,負責程序運行的內存分配;每個進程都有本身獨立的虛擬內存空間;

  • 線程:線程是進程中一個獨立的執行路徑(控制單元);一個進程中至少包含一條線程,即主線程。

  • 隊列:dispatch_queue_t,一種先進先出的數據結構,線程的建立和回收不須要程序員操做,由隊列負責。

  • 隊列-串行隊列:隊列中的任務只會順序執行

dispatch_queue_t q = dispatch_queue_create(「....」, dispatch_queue_serial);

  • 隊列-並行隊列:隊列中的任務一般會併發執行

dispatch_queue_t q = dispatch_queue_create("......", dispatch_queue_concurrent);

  • 隊列-全局隊列:是系統開發的,直接拿過來(get)用就能夠;與並行隊列相似,但調試時,沒法確認操做所在隊列  

dispatch_queue_t q = dispatch_get_global_queue(dispatch_queue_priority_default, 0);

  • 隊列-主隊列:每個應用程序對應惟一一個主隊列,直接get便可;在多線程開發中,使用主隊列更新UI

dispatch_queue_t q = dispatch_get_main_queue();

  • 串行隊列同步:操做不會新建線程、操做順序執行;

  • 串行隊列異步:操做須要一個子線程,會新建線程、線程的建立和回收不須要程序員參與,操做順序執行,是最安全的選擇;

  • 並行隊列同步:操做不會新建線程、操做順序執行;

  • 並行隊列異步:操做會新建多個線程(有多少任務,就開n個線程執行)、操做無序執行;隊列前若是有其餘任務,會等待前面的任務完成以後再執行;場景:既不影響主線程,又不須要順序執行的操做

  • 全局隊列同步:操做不會新建線程、操做順序執行;

  • 全局隊列異步:操做會新建多個線程、操做無序執行,隊列前若是有其餘任務,會等待前面的任務完成以後再執行;

  • 主局隊列同步:若是把主線程中的操做當作一個大的block,那麼除非主線程被用戶殺掉,不然永遠不會結束;主隊列中添加的同步操做永遠不會被執行,會死鎖;

  • 主局隊列異步:操做都應該在主線程上順序執行的,不存在異步的;

一、隊列和線程的區別:

隊列:是管理線程的,至關於線程池,能管理線程何時執行。

隊列分爲串行隊列和並行隊列:

串行隊列:隊列中的線程按順序執行(不會同時執行)

並行隊列:隊列中的線程會併發執行,可能會有一個疑問,隊列不是先進先出嗎,若是後面的任務執行完了,怎麼出去的了。這裏須要強調下,任務執行完畢了,不必定出隊列。只有前面的任務執行完了,纔會出隊列。

二、主線程隊列和gcd建立的隊列也是有區別的。

主線程隊列和gcd建立的隊列是不一樣的。在gcd中建立的隊列優先級沒有主隊列高,因此在gcd中的串行隊列開啓同步任務裏面沒有嵌套任務是不會阻塞主線程,只有一種可能致使死鎖,就是串行隊列裏,嵌套開啓任務,有可能會致使死鎖。

主線程隊列中不能開啓同步,會阻塞主線程。只能開啓異步任務,開啓異步任務也不會開啓新的線程,只是下降異步任務的優先級,讓cpu空閒的時候纔去調用。而同步任務,會搶佔主線程的資源,會形成死鎖。

三、線程:裏面有很是多的任務(同步,異步)

同步與異步的區別:

同步任務優先級高,在線程中有執行順序,不會開啓新的線程。

異步任務優先級低,在線程中執行沒有順序,看cpu閒不閒。在主隊列中不會開啓新的線程,其餘隊列會開啓新的線程。

四、主線程隊列注意:

在主隊列開啓異步任務,不會開啓新的線程而是依然在主線程中執行代碼塊中的代碼。爲何不會阻塞線程?

主隊列開啓異步任務,雖然不會開啓新的線程,可是他會把異步任務下降優先級,等閒着的時候,就會在主線程上執行異步任務。

在主隊列開啓同步任務,爲何會阻塞線程?

在主隊列開啓同步任務,由於主隊列是串行隊列,裏面的線程是有順序的,先執行完一個線程才執行下一個線程,而主隊列始終就只有一個主線程,主線程是不會執行完畢的,由於他是無限循環的,除非關閉應用開發程序。所以在主線程開啓一個同步任務,同步任務會想搶佔執行的資源,而主線程任務一直在執行某些操做,不願放手。兩個的優先級都很高,最終致使死鎖,阻塞線程了。

相關文章
相關標籤/搜索