1.多線程相關(guān)的幾個(gè)概念
1.1 任務(wù)
任務(wù):就是執(zhí)行操作的意思厂抽,換句話說就是你在線程中執(zhí)行的那段代碼浪秘。在GCD中是放在block中的蒋情。執(zhí)行任務(wù)有兩種方式:同步執(zhí)行和異步執(zhí)行。兩者的主要區(qū)別是是否具備開啟新線程的能力耸携。
- 同步:不具備開啟新線程的能力棵癣,只能在當(dāng)前線程中執(zhí)行任務(wù)。
- 異步:具備開啟新線程的能力夺衍,可以在新的線程中執(zhí)行任務(wù)浙巫。
注意:異步任務(wù)具備開啟新線程能力,僅僅表示有這個(gè)能力,但是不一定會(huì)開啟新的線程的畴,下面會(huì)詳細(xì)說明各種情況渊抄。
1.2 隊(duì)列
隊(duì)列:這里的隊(duì)列指任務(wù)隊(duì)列,即用來存放任務(wù)的隊(duì)列丧裁。隊(duì)列是一種特殊的線性表护桦,采用FIFO(先進(jìn)先出)的原則,即新任務(wù)總是被插入到隊(duì)列的末尾煎娇,而讀取任務(wù)的時(shí)候總是從隊(duì)列的頭部開始讀取二庵。每讀取一個(gè)任務(wù),則從隊(duì)列中釋放一個(gè)任務(wù)缓呛。在GCD中有兩種隊(duì)列:串行隊(duì)列和并行隊(duì)列催享。
- 串行隊(duì)列:隊(duì)列中的任務(wù)是一個(gè)接一個(gè)的執(zhí)行,按照FIFO的原則哟绊,先添加的任務(wù)先執(zhí)行
- 并行隊(duì)列:隊(duì)列中的任務(wù)可以同時(shí)(并行)執(zhí)行
2.GCD的使用
2.1 獲取隊(duì)列的幾種方式
- 獲取系統(tǒng)主線程中的主隊(duì)列(串行隊(duì)列)
//獲取主隊(duì)列
dispatch_queue_main_t mainQ = dispatch_get_main_queue();
- 獲取系統(tǒng)提供的全局隊(duì)列(并行隊(duì)列)
//獲取全局并行隊(duì)列
dispatch_queue_global_t globalQ = dispatch_get_global_queue(0, 0);
- 創(chuàng)建串行隊(duì)列
//創(chuàng)建串行隊(duì)列
dispatch_queue_t serialQ = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
- 創(chuàng)建并行隊(duì)列
//創(chuàng)建并行隊(duì)列
dispatch_queue_t concurrentQ = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
2.2 任務(wù)創(chuàng)建的方式
- 同步執(zhí)行
dispatch_queue_global_t queue = dispatch_get_global_queue(0, 0);
dispatch_sync(queue, ^{
// 執(zhí)行任務(wù)的代碼寫這里
});
- 異步執(zhí)行
dispatch_queue_global_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
// 執(zhí)行任務(wù)的代碼寫這里
});
2.3 任務(wù)和隊(duì)列的組合方式
并行隊(duì)列 | 串行隊(duì)列 | 主隊(duì)列 | |
---|---|---|---|
同步執(zhí)行 | 否 |
否 |
否 |
異步執(zhí)行 | 可以開啟多條新線程 | 只能開啟一條新線程 | 否 |
"否"
表示不能開啟新線程
代碼論證
//獲取主隊(duì)列
dispatch_queue_main_t mainQ = dispatch_get_main_queue();
//創(chuàng)建串行隊(duì)列
dispatch_queue_t serialQ = dispatch_queue_create("com.example.serial", DISPATCH_QUEUE_SERIAL);
//創(chuàng)建并行隊(duì)列
dispatch_queue_t concurrentQ = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
- 同步 + 并行
dispatch_sync(concurrentQ, ^{
NSLog(@"---------任務(wù)1--------%@",[NSThread currentThread]);
});
dispatch_sync(concurrentQ, ^{
NSLog(@"---------任務(wù)2--------%@",[NSThread currentThread]);
});
dispatch_sync(concurrentQ, ^{
NSLog(@"---------任務(wù)3--------%@",[NSThread currentThread]);
});
// ---------任務(wù)1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任務(wù)2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任務(wù)3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
- 同步 + 串行
dispatch_sync(serialQ, ^{
NSLog(@"---------任務(wù)1--------%@",[NSThread currentThread]);
});
dispatch_sync(serialQ, ^{
NSLog(@"---------任務(wù)2--------%@",[NSThread currentThread]);
});
dispatch_sync(serialQ, ^{
NSLog(@"---------任務(wù)3--------%@",[NSThread currentThread]);
});
// ---------任務(wù)1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任務(wù)2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任務(wù)3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
- 同步 + 主隊(duì)列
dispatch_async(serialQ, ^{
dispatch_sync(mainQ, ^{
NSLog(@"---------任務(wù)1--------%@",[NSThread currentThread]);
});
dispatch_sync(mainQ, ^{
NSLog(@"---------任務(wù)2--------%@",[NSThread currentThread]);
});
dispatch_sync(mainQ, ^{
NSLog(@"---------任務(wù)3--------%@",[NSThread currentThread]);
});
// ---------任務(wù)1--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任務(wù)2--------<NSThread: 0x600002b140c0>{number = 1, name = main}
// ---------任務(wù)3--------<NSThread: 0x600002b140c0>{number = 1, name = main}
});
- 異步 + 并行
dispatch_async(concurrentQ, ^{
NSLog(@"---------任務(wù)1--------%@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
NSLog(@"---------任務(wù)2--------%@",[NSThread currentThread]);
});
dispatch_async(concurrentQ, ^{
NSLog(@"---------任務(wù)3--------%@",[NSThread currentThread]);
});
// ---------任務(wù)1--------<NSThread: 0x600000075100>{number = 7, name = (null)}
// ---------任務(wù)2--------<NSThread: 0x60000007a800>{number = 3, name = (null)}
// ---------任務(wù)3--------<NSThread: 0x600000001ac0>{number = 4, name = (null)}
- 異步 + 串行
dispatch_async(serialQ, ^{
NSLog(@"---------任務(wù)1--------%@",[NSThread currentThread]);
});
dispatch_async(serialQ, ^{
NSLog(@"---------任務(wù)2--------%@",[NSThread currentThread]);
});
dispatch_async(serialQ, ^{
NSLog(@"---------任務(wù)3--------%@",[NSThread currentThread]);
});
// ---------任務(wù)1--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
// ---------任務(wù)2--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
// ---------任務(wù)3--------<NSThread: 0x6000038f5740>{number = 5, name = (null)}
- 異步 + 主隊(duì)列
dispatch_async(mainQ, ^{
NSLog(@"---------任務(wù)1--------%@",[NSThread currentThread]);
});
dispatch_async(mainQ, ^{
NSLog(@"---------任務(wù)2--------%@",[NSThread currentThread]);
});
dispatch_async(mainQ, ^{
NSLog(@"---------任務(wù)3--------%@",[NSThread currentThread]);
});
// ---------任務(wù)1--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
// ---------任務(wù)2--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
// ---------任務(wù)3--------<NSThread: 0x600001b08ec0>{number = 1, name = main}
2.3 GCD的應(yīng)用
- dispatch_after
用于指定時(shí)間之后執(zhí)行任務(wù)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"3秒鐘過后執(zhí)行");
});
- dispatch_barrier
異步并行執(zhí)行任務(wù)是無序的因妙,有時(shí)候需要控制他們的執(zhí)行順序,可以使用
dispatch_queue_t queue = dispatch_queue_create("com.example.concurrent", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"1-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"1----??-----%d",i);
}
});
dispatch_async(queue, ^{
NSLog(@"2-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"2----??-----%d",i);
}
});
dispatch_barrier_sync(queue, ^{
NSLog(@"3-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"3----你的心像一道墻-----%d",i);
}
});
dispatch_async(queue, ^{
NSLog(@"4-------%@",[NSThread currentThread]);
for (int i=0; i<3; i++) {
NSLog(@"4----??-----%d",i);
}
});
- dispatch_group_enter票髓、dispatch_group_leave
當(dāng)有多個(gè)網(wǎng)絡(luò)請求同時(shí)發(fā)送的時(shí)候攀涵,希望所有請求回調(diào)都結(jié)束再處理一些邏輯,可以使用 dispatch_group_enter洽沟、dispatch_group_leave
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_group_t group = dispatch_group_create();
// 當(dāng)有多個(gè)網(wǎng)絡(luò)請求同時(shí)發(fā)送的時(shí)候以故,希望所有請求回調(diào)都結(jié)束再處理一些邏輯,可以使用 dispatch_group_enter裆操、dispatch_group_leave
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i++) {
sleep(1); //模擬異步耗時(shí)操作
NSLog(@"async1 ------ %ld ----- %@",i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i++) {
sleep(1); //模擬異步耗時(shí)操作
NSLog(@"async2 ------ %ld ----- %@",i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
for (NSInteger i = 0; i < 3; i++) {
sleep(1); //模擬異步耗時(shí)操作
NSLog(@"async3 ------ %ld ----- %@",i,[NSThread currentThread]);
}
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"終于到我了 --- %@",[NSThread currentThread]);
});
- dispatch_semaphore_t 信號(hào)量
dispatch_semaphore_create(0) 創(chuàng)建一個(gè)信號(hào)量
dispatch_semaphore_wait 信號(hào)量減1
dispatch_semaphore_signal 信號(hào)量加1
//創(chuàng)建信號(hào)量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
dispatch_async(dispatch_get_global_queue(0, 0), ^{
for (int i=0; i<7; i++) {
sleep(1.5);
NSLog(@"----??-----%d",i);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (int i=0; i<7; i++) {
sleep(1.5);
NSLog(@"----??-----%d",i);
}
dispatch_semaphore_signal(semaphore);
});
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
for (int i=0; i<7; i++) {
sleep(1.5);
NSLog(@"----??-----%d",i);
}
});
- gcd定時(shí)器
注意:要用一個(gè)變量引用 timer 怒详,否則會(huì)在方法執(zhí)行完就釋放了。
@property (nonatomic, strong) dispatch_source_t timer;
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
self.timer = timer;// @property (nonatomic, strong) dispatch_source_t timer;
// 第二個(gè)參數(shù)是開始時(shí)間踪区,第四個(gè)參數(shù)是精確度昆烁,0表示沒有誤差
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 1 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
NSLog(@"123123123");
});
dispatch_resume(timer);
3.多線程常見面試題
3.1 進(jìn)程和線程的概念?
==進(jìn)程==: 在iOS中,可以看做一個(gè)正在運(yùn)行的程序朽缴,一個(gè)app只能有一個(gè)進(jìn)程善玫。
==線程==: 線程是進(jìn)程的基本執(zhí)行單元,作用是執(zhí)行進(jìn)程中的代碼密强,一個(gè)進(jìn)程最少有一個(gè)線程茅郎,叫做主線程。
3.2 同步和異步的區(qū)別或渤?
同步不具備開啟新線程的能力系冗,異步具備開啟新線程的能力。
3.3 多線程的原理薪鹦?
單核CPU同一時(shí)刻只能處理1條線程掌敬,只有1條線程在工作(執(zhí)行)惯豆,多線程并發(fā)(同時(shí))執(zhí)行,其實(shí)是CPU快速地在多條線程之間調(diào)度(切換),如果CPU調(diào)度線程的時(shí)間足夠快奔害,就造成了多線程并發(fā)執(zhí)行的假象
3.4 什么情況下會(huì)造成死鎖楷兽?
在當(dāng)前串行隊(duì)列所在的線程中,同步的向當(dāng)前串行隊(duì)列添加一個(gè)新的任務(wù)华临,就會(huì)產(chǎn)生死鎖芯杀。
例如: 在主線程中,同步向主隊(duì)列中添加一個(gè)任務(wù)就會(huì)造成死鎖