07.GCD函式和佇列組合示例
阿新 • • 發佈:2019-01-04
1.GCD基本使用(非同步函式+併發佇列)
- 可以看到,系統自動給開闢了新的子執行緒來執行這三個任務,且由於時併發佇列,系統開闢了三個子執行緒
- 注意:開闢多少個子執行緒由系統決定,三個任務依次從佇列中取出放在不同的子執行緒執行,但是由於CPU排程子執行緒是由系統控制的,所以任務執行的順序不定,並沒有違背佇列中任務的先進先出原則
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
// 1.建立佇列
/* 引數解釋
第一個引數:佇列名稱
第二個引數:佇列屬性(序列還是併發)
DISPATCH_QUEUE_SERIAL :序列
DISPATCH_QUEUE_CONCURRENT 併發
*/
// 1.1建立一個併發佇列
// dispatch_queue_t queue = dispatch_queue_create("zj.queue", DISPATCH_QUEUE_CONCURRENT);
// 1.2系統內部已經給我們提供好了一個現成的併發佇列
/*
第一個引數: iOS8以前是優先順序, iOS8以後是服務質量
iOS8以前
* - DISPATCH_QUEUE_PRIORITY_HIGH 高優先順序 2
* - DISPATCH_QUEUE_PRIORITY_DEFAULT: 預設的優先順序 0
* - DISPATCH_QUEUE_PRIORITY_LOW: 低優先順序 -2
* - DISPATCH_QUEUE_PRIORITY_BACKGROUND: 後臺
iOS8以後
* - QOS_CLASS_USER_INTERACTIVE 0x21 使用者互動(使用者迫切想執行任務)
* - QOS_CLASS_USER_INITIATED 0x19 使用者需要
* - QOS_CLASS_DEFAULT 0x15 預設
* - QOS_CLASS_UTILITY 0x11 工具(低優先順序, 蘋果推薦將耗時操作放到這種型別的佇列中)
* - QOS_CLASS_BACKGROUND 0x09 後臺
* - QOS_CLASS_UNSPECIFIED 0x00 沒有設定
第二個引數: Flags that are reserved for future use. Always specify 0 for this parameter.
根據蘋果文件,可以知道這是一個保留值,以便蘋果在系統內部執行一些操作,並且蘋果建議我們始終傳入0即可.
*/
// 為了同時適配iOS8以及iOS8以前的版本,優先順序引數直接傳0即可
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.新增任務到佇列(這裡使用非同步方式)
/* 引數解釋
第一個引數:需要將任務新增到哪個佇列
第二個引數:需要執行的任務
*/
// 連續新增三個任務
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_async (queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]);
});
/* 結果:(每次列印的執行緒number順序不一致)
<NSThread: 0x7fb878d02110>{number = 4, name = (null)}
<NSThread: 0x7fb878e21500>{number = 3, name = (null)}
<NSThread: 0x7fb878f91e20>{number = 2, name = (null)}
*/
}
2.GCD基本使用(非同步函式+序列佇列)
- 可以看到,開啟了新的執行緒,但是隻開啟了一條
- 能夠建立新執行緒的原因:使用”非同步”函式呼叫
- 只建立1個子執行緒的原因:佇列是序列佇列
- 由於佇列序列佇列,所以任務是從上到下依次執行
- 由於使用了非同步函式,那麼不會等到非同步函式中的任務執行完畢再去執行後面的程式碼
- (void)asynSerial{
// 1.建立序列佇列
dispatch_queue_t queue = dispatch_queue_create("zj.queue", DISPATCH_QUEUE_SERIAL);
// 2.新增任務到佇列
// 連續新增三個任務
dispatch_async(queue, ^{
NSLog(@"任務一執行,%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務二執行,%@",[NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"任務三執行,%@",[NSThread currentThread]);
});
NSLog(@"+++++++");
/* 列印結果:
任務一執行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
+++++++
任務二執行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
任務三執行,<NSThread: 0x7fdb91720450>{number = 2, name = (null)}
*/
}
3.GCD基本使用(同步函式+序列佇列)
- 可以看到不會開啟新的執行緒,且執行緒中的任務會依次執行
- 因為呼叫了同步函式, 那麼會等同步函式中的任務執行完畢, 才會執行後面的程式碼
- (void)syncSerial
{
// 1.建立一個序列佇列
// #define DISPATCH_QUEUE_SERIAL NULL
// 所以可以直接傳NULL
dispatch_queue_t queue = dispatch_queue_create("zj.queue", NULL);
// 2.將任務新增到佇列中
dispatch_sync(queue, ^{
NSLog(@"任務1 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務2 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務3 == %@", [NSThread currentThread]);
});
NSLog(@"---------");
/*輸出結果:
任務1 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任務2 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任務3 == <NSThread: 0x7fe129512690>{number = 1, name = main}
---------
*/
}
4.GCD基本使用(同步函式+併發佇列)
- 使用了同步函式,不會建立新的執行緒
/*
同步 + 併發 : 不會開啟新的執行緒
*/
- (void)syncConCurrent
{
// 1.建立一個併發佇列
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
// 2.將任務新增到佇列中
dispatch_sync(queue, ^{
NSLog(@"任務1 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務2 == %@", [NSThread currentThread]);
});
dispatch_sync(queue, ^{
NSLog(@"任務3 == %@", [NSThread currentThread]);
});
NSLog(@"---------");
/*輸出結果:
任務1 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任務2 == <NSThread: 0x7fe129512690>{number = 1, name = main}
任務3 == <NSThread: 0x7fe129512690>{number = 1, name = main}
---------
*/
}
5.GCD基本使用(非同步函式+主佇列)
- 只要是在主佇列,那麼無論是呼叫同步還是非同步函式,都不會開啟新執行緒,一定會在主執行緒中執行
/*
非同步 + 主佇列 : 不會建立新的執行緒,並且任務是在主執行緒中執行
*/
- (void)asyncMain
{
// 建立主佇列
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"%@", [NSThread currentThread]);
});
}
/* 輸出結果:
任務一 <NSThread: 0x7fa1d35267c0>{number = 1, name = main}
任務二 <NSThread: 0x7fa1d35267c0>{number = 1, name = main}
*/
6.GCD基本使用(同步函式+主佇列)-死鎖
- 如果是在主執行緒中呼叫同步函式 + 主佇列, 那麼會導致死鎖
導致死鎖的原因:
- sync函式是在主執行緒中執行的, 並且會等待block執行完畢. 先呼叫
- block是新增到主佇列的, 也需要在主執行緒中執行. 後呼叫
- 由於佇列的先進先出原則,那麼sync函式要先執行,但是他又要等待block執行完畢才會往後繼續執行,block又在主佇列的後面,等待同步函式執行完畢後才會執行,所以兩個任務相互等待,永遠都不會往下繼續執行,造成死鎖
/*
在主執行緒中呼叫同步函式+主佇列會導致死鎖
*/
- (void)syncMain
{
NSLog(@"%@", [NSThread currentThread]);
// 主佇列:
dispatch_queue_t queue = dispatch_get_main_queue();
// 如果是呼叫 同步函式, 那麼會等同步函式中的任務執行完畢, 才會執行後面的程式碼
// 注意: 如果dispatch_sync方法是在主執行緒中呼叫的, 並且傳入的佇列是主佇列, 那麼會導致死鎖
dispatch_sync(queue, ^{
NSLog(@"----------");
NSLog(@"%@", [NSThread currentThread]);
});
NSLog(@"----------");
}
/* 輸出結果:佇列中的任務以及主執行緒中後續的操作不再執行
<NSThread: 0x7fadbad1ba70>{number = 1, name = main}
*/
7.GCD基本使用(同步函式+主佇列)-不死鎖
- 要想使用同步函式和主佇列,且不造成死鎖,可以在子執行緒中執行同步函式
/*
如果是在子執行緒中呼叫 同步函式 + 主佇列, 那麼沒有任何問題
*/
- (void)syncMain2
{
dispatch_queue_t queue = dispatch_get_global_queue(0, 0);
dispatch_async(queue, ^{
// block會在子執行緒中執行
// NSLog(@"%@", [NSThread currentThread]);
dispatch_queue_t queue = dispatch_get_main_queue();
dispatch_sync(queue, ^{
// block一定會在主執行緒執行
NSLog(@"%@", [NSThread currentThread]);
});
});
NSLog(@"------------");
/* 輸出結果:
------------
<NSThread: 0x7fd16af280d0>{number = 1, name = main}
*/
}