在iOS開發中,蘋果提供了三種多線程技術,分別是:
(1)NSThread
(2)NSOperation
(3)GCD
簡單介紹一下GCD的使用。
GCD全稱 Grand Central Dispatch,可以稱之為大中央調度。實際上GCD是管理著一個線程池,如何創建線程,如何回收線程,以及分配多少個線程,這些都是GCD來控制的。在開發中,程序員是不用操作線程的相關事情,程序員只需要把應該做的操作放到相應的隊列裡面即可。
一:自定義隊列
GCD中有多種隊列,其中自定義的隊列有兩種:串行隊列和並行隊列
1:串行隊列:隊列中的任務只會順序執行,且一次只能夠執行一個任務。也就是說,執行完一個任務後,才會執行下一個任務。
2:並行隊列:可以一次執行多個任務。比如說並行隊列中有10個任務,可以一次執行3個任務,這三個任務哪個先執行完了,再接著執行剩下的任務。
注意:無論是串行隊列還是並行隊列,他們都是FIFO(先進先出)的。也就是說,無論是哪種隊列,任務進隊列的時間越早,其執行的時間就越早(只不過某些情況下任務執行的結束時間是不確定的)。
GCD中有兩種操作,分別是同步操作和異步操作
1:同步操作:不會新開線程
2:異步操作:會開啟新的線程
兩種操作和兩種隊列,組合為4種情況,實際上,在開發中,有些組合基本上是不會用到的。下面用程序描述一下四種組合。
組合一:串行隊列+同步操作(不會新建線程,而且任務是一個一個的執行,因此實際上就是順序執行),代碼如下:
- (void)gcdDemo1
{
//串行隊列+同步操作
dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_SERIAL);
for(int i = 0; i < 10; ++i){
dispatch_sync(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
number = 1,說明是主線程,沒有新開線程。
組合二:串行隊列+異步操作(因為任務要一個一個的執行,但是因為是異步操作,所以會開啟一個新的線程,所有的任務都在新的線程上執行),代碼如下:
- (void)gcdDemo1
{
dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_SERIAL);
//串行隊列+異步操作
for (int i = 0; i < 10; ++i){
dispatch_async(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
number = 2,說明開啟了一個新的子線程,但仍然是順序執行。
組合三:並行隊列+同步操作(因為同步操作不會開啟新的線程,因此,即使並行隊列可以一次開始多個任務,但實際上仍舊是每個任務都在主線程上執行,且按順序執行)。代碼如下:
- (void)gcdDemo2
{
dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_CONCURRENT);
//並行隊列+同步任務
for(int i = 0; i < 10; ++i){
dispatch_sync(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
沒有開啟新的線程,且按順序執行。
組合四:並行隊列+異步操作(並行隊列會一次開始多個任務,且異步操作可以開啟新的線程,因此同一時刻可能會同時執行多個任務,開啟多個線程,且每個任務的結束時間是不確定的)。代碼如下:
- (void)gcdDemo2
{
dispatch_queue_t queue = dispatch_queue_create("gcddemo", DISPATCH_QUEUE_CONCURRENT);
//並行隊列+異步任務
for(int i = 0; i < 10; ++i){
dispatch_async(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
可以看到,開啟了多個線程,且任務不是按順序執行完的。
二:全局隊列
為了方便開發,蘋果還提供了有全局隊列,全局隊列實際上是並行隊列,因此,全局隊列的執行結果和並行隊列的執行結果是一致的。代碼如下:
全局隊列+同步任務:
- (void)gcdDemo3
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//全局隊列+同步任務
for (int i = 0; i < 10; ++i){
//同步任務
dispatch_sync(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
全局隊列+異步任務:
- (void)gcdDemo3
{
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//全局隊列+異步任務
for(int i = 0; i < 10; ++i){
dispatch_async(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
三:主隊列
蘋果還提供了一種隊列是主隊列,主隊列是串行隊列,但是和串行隊列又有差異。主隊列上的任務都應該在主線程上順序執行,沒有異步的概念。也就是說,即使是異步任務在主隊列上執行,也不會開啟新的線程。
主隊列+異步任務:
- (void)gcdDemo4
{
dispatch_queue_t queue = dispatch_get_main_queue();
//主隊列+異步任務
for(int i = 0; i < 10; ++i){
dispatch_async(queue,^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
執行結果:
可以看到,沒有開啟新的線程,且是順序執行。
主隊列+同步任務(會阻塞線程),代碼如下:
- (void)gcdDemo4
{
dispatch_queue_t queue = dispatch_get_main_queue();
//主隊列+同步任務,會阻塞
for(int i = 0; i < 10; ++i){
dispatch_sync(queue, ^{
NSLog(@"%@ %d",[NSThread currentThread],i);
});
}
}
阻塞原因:
主隊列中本身是有一個任務A的(主任務),且該任務A還沒有執行完。在執行任務A的過程中,又插入了新的同步任務B。我們知道,串行隊列中,必須先執行完一個任務後,才能繼續執行另一個任務。此時的情況時:
若想繼續執行任務A,需要先把任務B執行完,若想繼續執行任務B,需要先把任務A執行完,因此造成了阻塞。
在開發中,應該避免這種阻塞的情況。