iOS開發-佇列和同步非同步執行
序列佇列,併發佇列,全域性佇列(併發),主佇列(序列)。
執行的方法有:同步執行和非同步執行。
提到多執行緒:pthread,NSThread,GCD,NSOperation
其中phtread是跨平臺的。 GCD和NSOperation都是常用的,後者是基於前者的。
兩者區別: GCD的核心概念是將一個任務新增到佇列,指定任務執行的方法,然後執行。 NSOperation則是直接將一個操作新增到佇列中。
1.序列佇列,同步執行
/建立序列佇列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", NULL );
// 執行任務
for (int i = 0; i<10; i++)
{
dispatch_sync(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"序列佇列 同步執行");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
執行結果:
執行結果可以看到全在主執行緒執行,並且是按照數序執行,迴圈結束之後主執行緒的列印才輸出。
2.序列佇列,非同步執行
//建立序列佇列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", NULL);
// 執行任務
for (int i = 0; i<10; i++)
{
dispatch_async(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"序列佇列 非同步執行");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
執行結果:
結果顯示,系統開了1條非同步執行緒,因此全部線上程3執行,並且是順序執行。主執行緒列印雖然在最上面,但是這個先後順序是不確定,有可能會混在中間。
3.併發佇列,同步執行
//建立併發佇列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
// 執行任務
for (int i = 0; i<10; i++)
{
dispatch_sync(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"併發佇列 同步執行");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
執行結果: 這個執行結果和第1種的(序列佇列,同步執行)是一模一樣的。 因為同步任務的概念就是按順序執行,後面都要等。言外之意就是不允許多開執行緒。所以一旦是同步執行,前面什麼佇列已經沒區別了。
4.併發佇列,非同步執行
//建立併發佇列
dispatch_queue_t testqueue = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
// 執行任務
for (int i = 0; i<10; i++)
{
dispatch_async(testqueue, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"併發佇列 非同步執行");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
系統開了多條執行緒,並且執行的順序也是亂序的
5.主佇列,同步執行
NSLog(@"之前 - %@", [NSThread currentThread]);
dispatch_sync(dispatch_get_main_queue(), ^{
NSLog(@"sync - %@", [NSThread currentThread]);
});
NSLog(@"之後 - %@", [NSThread currentThread]);
- 1
- 2
- 3
- 4
- 5
- 6
- 7
執行結果為卡死 卡死的原因是迴圈等待,主佇列的東西要等主執行緒執行完,而因為是同步執行不能開執行緒,所以下面的任務要等上面的任務執行完,所以卡死。
6.主佇列,非同步執行
// 主佇列 - 程式啟動之後已經存在主執行緒,主佇列同樣存在
dispatch_queue_t q = dispatch_get_main_queue();
// 安排一個任務
for (int i = 0; i<10; i++)
{
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
NSLog(@"sleep");
[NSThread sleepForTimeInterval:2.0];
NSLog(@"主佇列,非同步執行");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
主執行緒在睡會之後才打印,迴圈一直在等著。因為主佇列的任務雖然會加到主執行緒中執行,但是如果主執行緒裡也有任務就必須等主執行緒任務執行完才輪到主佇列的。
7.同步任務的使用
dispatch_queue_t q = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
// 1. 使用者登入,必須要第一個執行
dispatch_sync(q, ^{
[NSThread sleepForTimeInterval:2.0];
NSLog(@"使用者登入 %@", [NSThread currentThread]);
});
// 2. 扣費
dispatch_async(q, ^{
NSLog(@"扣費 %@", [NSThread currentThread]);
});
// 3. 下載
dispatch_async(q, ^{
NSLog(@"下載 %@", [NSThread currentThread]);
});
NSLog(@"同步任務的使用");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
“使用者登陸”在主執行緒列印,後兩個在非同步執行緒列印。 上面的“使用者登陸”使用同步執行,後面的扣費和下載都是非同步執行。所以“使用者登陸”必須第一個打印出來不管等多久,然後後面的兩個非同步和主執行緒列印會不確定順序的列印。這就是日常開發中,那些後面對其有依賴的必須要先執行的任務使用同步執行,然後反正都要執行先後順序無所謂的使用非同步執行。
8.block非同步任務包裹同步任務
dispatch_queue_t queue = dispatch_queue_create("queue2017", DISPATCH_QUEUE_CONCURRENT);
void (^task)() = ^ {
// 1. 使用者登入,必須要第一個執行
dispatch_sync(queue, ^{
NSLog(@"使用者登入 %@", [NSThread currentThread]);
});
// 2. 扣費
dispatch_async(queue, ^{
NSLog(@"扣費 %@", [NSThread currentThread]);
});
// 3. 下載
dispatch_async(queue, ^{
NSLog(@"下載 %@", [NSThread currentThread]);
});
};
dispatch_async(queue, task);
[NSThread sleepForTimeInterval:1.0];
NSLog(@"block非同步任務包裹同步任務");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
因為整個block是在非同步執行的,所以即使裡面“使用者登陸”是同步執行,那也無法在主執行緒中執行,只能開一條非同步執行緒執行,因為是同步的所以必須等他先執行,後面的“扣費”和“下載”在上面同步執行結束之後,不確定順序的列印。
9.全域性佇列
dispatch_queue_t q = dispatch_get_global_queue(0, 0);
for (int i = 0; i < 10; i++)
{
dispatch_async(q, ^{
NSLog(@"%@ %d", [NSThread currentThread], i);
});
}
[NSThread sleepForTimeInterval:1.0];
NSLog(@"全域性佇列");
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
全域性佇列的本質就是併發佇列,只是在後面加入了,“服務質量”,和“排程優先順序” 兩個引數,這兩個引數一般為了系統間的適配,最好直接填0和0。
10.總之
- 開不開執行緒:同步不開,非同步開。
- 開幾條執行緒:序列佇列開一條,併發開多條(非同步)
- 主佇列:專門用來在主執行緒上排程任務的”佇列”,主佇列不能在其他執行緒中排程任務!
- 如果主執行緒上當前正在有執行的任務,主佇列暫時不會排程任務的執行!主佇列同步任務,會造成死鎖。原因是迴圈等待
- 同步任務可以佇列排程多個非同步任務前,指定一個同步任務,讓所有的非同步任務,等待同步任務執行完成,這是依賴關係。
- 全域性佇列:併發,能夠排程多個執行緒,執行效率高,但是相對費電。 序列佇列效率較低,省電省流量,或者是任務之間需要依賴也可以使用序列佇列。
- 也可以通過判斷當前使用者的網路環境來決定開的執行緒數。WIFI下6條,3G/4G下2~3條。