iOS多線程使用總結(jié)(NSThread,GCD,NSOperation)
前文
本文主要用于學(xué)習(xí)記錄渠缕,概念問題簡單說一下鸽素,主要寫一些使用案例。
iOS 常用的多線程方案有3種.
- NSThread
- GCD
- NSOperation
其中用的最多的就是GCD了亦鳞,其實(shí)還有一種Pthreads馍忽,但是實(shí)在不常用,所以不太了解燕差,就不說了遭笋。
文章中主要使用Objective-C語言,示例代碼會(huì)用Swift翻譯過來徒探,如有錯(cuò)誤請(qǐng)指出瓦呼。
Swift打印的時(shí)候最好使用NSLog,這樣可以看到打印時(shí)間测暗,以及線程信息央串。
1. NSThread
NSThread面向?qū)ο螅容^直觀偷溺,但是需要手動(dòng)管理生命周期蹋辅,雖然不常用,但是有些方法還是比較好用的挫掏,比如[NSThread currentThread]
可以獲取當(dāng)前線程侦另,用來調(diào)試給常好用谦铃。后面我們會(huì)經(jīng)常用到眼滤。
- 創(chuàng)建+啟動(dòng)線程
Objective-C:
//套路:不要先直接alloc init 先看一下頭文件 類方法 如果沒有合適的類方法 才 init
// 1. 創(chuàng)建線程
- (void)createThread{
NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(task) object:nil];
//開啟線程
[thread start];
}
// 新線程調(diào)用方法挎挖,里邊為需要執(zhí)行的任務(wù)
- (void)task {
NSLog(@"%@", [NSThread currentThread]);
}
//-------------------------------------------------
Swift:
// 1. 創(chuàng)建線程
func createThread() {
let thread = Thread(target: self, selector: #selector(task), object: nil)
//啟動(dòng)
thread.start()
}
// 新線程調(diào)用方法胎撤,里邊為需要執(zhí)行的任務(wù)
@objc func task() {
NSLog("%@", Thread.current)
}
- 創(chuàng)建并自動(dòng)啟動(dòng)
Objective-C:
//類方法 -- 簡單 不能拿到對(duì)象 沒有機(jī)會(huì)進(jìn)行更加詳細(xì)的設(shè)置
[NSThread detachNewThreadSelector:@selector(task) toTarget:self withObject:nil];
- (void)task {
NSLog(@"%@", [NSThread currentThread]);
}
//-----------------------------------------------------
Swift:
//Swift寫法
Thread.detachNewThreadSelector(#selector(task), toTarget: self, with: nil)
@objc func task() {
NSLog("%@", Thread.current)
}
- 隱式創(chuàng)建并啟動(dòng)線程
Objective-C:
//隱式創(chuàng)建
[self performSelectorInBackground:@selector(task) withObject:nil];
//---------------------------------------------------------------------
Swift:
performSelector(inBackground: #selector(task), with: nil)
- 其他方法(只列OC方法)
//取消線程
- (void)cancel;
//啟動(dòng)線程
- (void)start;
//判斷某個(gè)線程的狀態(tài)的屬性
@property (readonly, getter=isExecuting) BOOL executing;
@property (readonly, getter=isFinished) BOOL finished;
@property (readonly, getter=isCancelled) BOOL cancelled;
//設(shè)置和獲取線程名字
-(void)setName:(NSString *)n;
-(NSString *)name;
//獲取當(dāng)前線程信息
+ (NSThread *)currentThread;
//獲取主線程信息
+ (NSThread *)mainThread;
// 判斷是否為主線程(對(duì)象方法)
- (BOOL)isMainThread;
// 判斷是否為主線程(類方法)
+ (BOOL)isMainThread;
//使當(dāng)前線程暫停一段時(shí)間魂仍,或者暫停到某個(gè)時(shí)刻
+ (void)sleepForTimeInterval:(NSTimeInterval)time;
+ (void)sleepUntilDate:(NSDate *)date;
- 線程之間的通信
例如:子線程下載任務(wù),完成后回到主線程刷新UI
// 在主線程上執(zhí)行操作
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray<NSString *> *)array;
// equivalent to the first method with kCFRunLoopCommonModes
// 在指定線程上執(zhí)行操作
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait modes:(NSArray *)array NS_AVAILABLE(10_5, 2_0);
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait NS_AVAILABLE(10_5, 2_0);
// 在當(dāng)前線程上執(zhí)行操作挖炬,調(diào)用 NSObject 的 performSelector:相關(guān)方法
- (id)performSelector:(SEL)aSelector;
- (id)performSelector:(SEL)aSelector withObject:(id)object;
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
示例DEMO:(Objective-C)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
//創(chuàng)建線程下載圖片
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(getIMG) object:nil];
[thread start];
}
- (void)getIMG{
[NSThread sleepForTimeInterval:5]; //模擬耗時(shí)任務(wù)
NSString *strAddress = @"http://pic41.nipic.com/20140501/2531170_162158900000_2.jpg";
NSURL *url = [NSURL URLWithString:strAddress];
NSData *data = [NSData dataWithContentsOfURL:url];
UIImage *image = [UIImage imageWithData:data];
// 回到主線程刷新界面
[self performSelectorOnMainThread:@selector(updateUI:) withObject:image waitUntilDone:NO];
NSLog(@"over");
}
//回到主線程刷新UI
- (void)updateUI:(UIImage *)image{
_imageView.image = image;
NSLog(@"current thread -- %@", [NSThread currentThread]);
}
示例DEMO:(Swift)
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let thread = Thread(target: self, selector: #selector(task), object: nil)
thread.start()
}
func getIMG() {
Thread.sleep(forTimeInterval: 5) //模擬耗時(shí)任務(wù)
let strAddress = "http://pic41.nipic.com/20140501/2531170_162158900000_2.jpg"
let url = URL.init(string: strAddress)
do {
let data = try Data(contentsOf: url!)
let image = UIImage(data: data)
performSelector(onMainThread: #selector(updateUI), with: image, waitUntilDone: false)
// 回到主線程刷新界面
NSLog("over")
} catch { }
}
@objc func updateUI(image: UIImage) {
NSLog("current thread -- %@", Thread.current);
}
2. GCD
- GCD簡介:略
- GCD好處:
GCD 可用于多核的并行運(yùn)算,最重要的是它會(huì)自動(dòng)管理線程的生命周期(創(chuàng)建線程支鸡、調(diào)度任務(wù)镰官、銷毀線程),非常方便. - GCD底層使用的是C語言.但是經(jīng)過蘋果封裝非常好用,老少咸宜.
2.1任務(wù)和隊(duì)列
-
任務(wù): 就是執(zhí)行操作的意思剧蚣,放在代碼里支竹,就是執(zhí)行一段代碼。在GCD中有一個(gè)Block鸠按,將要執(zhí)行的代碼(即任務(wù))放進(jìn)去即可礼搁。任務(wù)有兩種執(zhí)行方式:同步執(zhí)行和異步執(zhí)行,他們主要的區(qū)別:是否開啟新線程目尖。
同步執(zhí)行(sync):
- 不具備開線程的能力
- 同步添加到指定的隊(duì)列中去馒吴,在添加的任務(wù)執(zhí)行完成之前會(huì)一直等待,等待前邊的任務(wù)執(zhí)行完成之后才繼續(xù)執(zhí)行。
異步執(zhí)行(async):
- 具備開線程的能力
- 異步添加任務(wù)到執(zhí)行隊(duì)列中饮戳,它不會(huì)做任何等待豪治,可以繼續(xù)執(zhí)行任務(wù)
-
隊(duì)列:用于存放任務(wù)。一共有兩種隊(duì)列扯罐, 串行隊(duì)列 和 并行隊(duì)列负拟。
串行隊(duì)列
- 每次只有一個(gè)任務(wù)被執(zhí)行,任務(wù)是一個(gè)接著一個(gè)地執(zhí)行歹河。(只會(huì)開啟一個(gè)線程)
并發(fā)隊(duì)列
- 可以讓多個(gè)任務(wù)并發(fā)(同時(shí))執(zhí)行齿椅。(可以開啟多個(gè)線程,同時(shí)執(zhí)行任務(wù))
2.2隊(duì)列的創(chuàng)建以及獲取
隊(duì)列的創(chuàng)建:
Objective-C:
// 串行隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊(duì)列的創(chuàng)建方法
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_CONCURRENT);
//---------------------------------------------------
// 串行隊(duì)列的創(chuàng)建方法
let queue1 = dispatch_queue_serial_t(label: "gcd.name")
let queue2 = DispatchQueue(label: "gcd.name", attributes: DispatchQueue.Attributes.concurrent)
// 并發(fā)隊(duì)列的創(chuàng)建方法
let queue3 = dispatch_queue_concurrent_t(label: "gcd.name")
let queue4 = DispatchQueue(label: "gcd.name")
主隊(duì)列是一個(gè)比較特殊的串行隊(duì)列:
Objective-C:
// 主隊(duì)列獲取方法
dispatch_queue_t queue = dispatch_get_main_queue();
//------------------------------------------------
Swift:
//主隊(duì)列
let mainQueue = DispatchQueue.main;
GCD 還提供了一個(gè)全局并發(fā)隊(duì)列:
Objective-C:
// 全局并發(fā)隊(duì)列的獲取方法
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//--------------------------------------------------------
Swift:
// 全局并發(fā)隊(duì)列的獲取方法
let globalQueue = DispatchQueue.global()
2.3任務(wù)的創(chuàng)建
同步執(zhí)行任務(wù)的創(chuàng)建方法: dispatch_sync
異步執(zhí)行任務(wù)創(chuàng)建方法 :dispatch_async启泣。
Objective-C:
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
// 這里放同步執(zhí)行任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
// 這里放異步執(zhí)行任務(wù)代碼
});
//------------------------------------------------
Swift
// 同步執(zhí)行任務(wù)創(chuàng)建方法
queue.sync {
// 這里放同步執(zhí)行任務(wù)代碼
}
// 異步執(zhí)行任務(wù)創(chuàng)建方法
queue2.async {
// 這里放異步執(zhí)行任務(wù)代碼
}
2.4任務(wù)和隊(duì)列不同的組合方式
GCD提供了兩種任務(wù)執(zhí)行方式(同步執(zhí)行和異步執(zhí)行),提供了兩種隊(duì)列:(串行隊(duì)列和并行隊(duì)列)示辈,在加上主隊(duì)列寥茫,故而就有多種不同的組合方式。
1.同步執(zhí)行 + 串行隊(duì)列
2.同步執(zhí)行 + 并發(fā)隊(duì)列
3.異步執(zhí)行 + 串行隊(duì)列
4.異步執(zhí)行 + 并發(fā)隊(duì)列
5.同步執(zhí)行 + 主隊(duì)列
6.異步執(zhí)行 + 主隊(duì)列
區(qū)別:
區(qū)別 | 并發(fā)隊(duì)列 | 串行隊(duì)列 | 主隊(duì)列 |
---|---|---|---|
同步(sync) | 沒有開啟新線程矾麻,串行隊(duì)列 | 沒有開啟新線程纱耻,串行執(zhí)行任務(wù) | 死鎖,不執(zhí)行 |
異步(async) | 開啟新線程,并發(fā)執(zhí)行任務(wù) | 開啟新線程(1條)险耀,串行執(zhí)行任務(wù) | 沒有開啟新線程弄喘,串行執(zhí)行任務(wù) |
2.5 GCD的基本使用(6種組合)
每種情況,我們簡單介紹一下甩牺,直接代碼+打印結(jié)果+結(jié)論蘑志,展示出來
這里的代碼以及結(jié)論參考了行走少年郎的一篇多線程文章,寫的很好贬派,比我的好急但,所以就把我原來的替換了。大家可以去看他寫的搞乏。
2.5.1 同步執(zhí)行 + 串行隊(duì)列
- 不會(huì)開啟新線程波桩,在當(dāng)前線程執(zhí)行任務(wù),串行執(zhí)行任務(wù)请敦。
Objective-C:
//打印當(dāng)前線程
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);
NSLog(@"同步-串行 --- begin");
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_SERIAL);
//執(zhí)行任務(wù)
dispatch_sync(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"同步-串行 --- end");
Swift:
//打印當(dāng)前線程
NSLog("當(dāng)前線程 --- %@",Thread.current);
NSLog("同步-串行 --- begin");
let queue = dispatch_queue_serial_t(label: "gcd.name")
//執(zhí)行任務(wù)
queue.sync {
//追加任務(wù) 1
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("1 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 2
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("2 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 3
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("3 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 4
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("4 --- %@",Thread.current); //打印當(dāng)前線程
}
NSLog("同步-串行 --- end");
打印結(jié)果:
2020-08-18 15:09:05.019655+0800 KX_GCD_Demo[27130:517117] 當(dāng)前線程 --- <NSThread: 0x600002134f00>{number = 1, name = main}
2020-08-18 15:09:05.019782+0800 KX_GCD_Demo[27130:517117] 同步-串行 --- begin
2020-08-18 15:09:07.021050+0800 KX_GCD_Demo[27130:517117] 1 --- <NSThread: 0x600002134f00>{number = 1, name = main}
2020-08-18 15:09:09.021304+0800 KX_GCD_Demo[27130:517117] 2 --- <NSThread: 0x600002134f00>{number = 1, name = main}
2020-08-18 15:09:11.021541+0800 KX_GCD_Demo[27130:517117] 3 --- <NSThread: 0x600002134f00>{number = 1, name = main}
2020-08-18 15:09:13.022790+0800 KX_GCD_Demo[27130:517117] 4 --- <NSThread: 0x600002134f00>{number = 1, name = main}
2020-08-18 15:09:13.022947+0800 KX_GCD_Demo[27130:517117] 同步-串行 --- end
結(jié)論:
- 所有任務(wù)都在當(dāng)前線程即主線程中執(zhí)行,沒有開啟新的線程(同步執(zhí)行不具備開啟線程的能力)
- 所有的任務(wù)都在
begin
和end
之間執(zhí)行,說明同步執(zhí)行任務(wù)需要等待隊(duì)列中的任務(wù)執(zhí)行結(jié)束 - 任務(wù)是按照1,2,3,4順序來執(zhí)行的,說明串行隊(duì)列每次只能執(zhí)行一個(gè)任務(wù),需要一個(gè)接著一個(gè)按順序執(zhí)行
2.5.2 同步執(zhí)行 + 并發(fā)隊(duì)列
- 在當(dāng)前線程中執(zhí)行任務(wù)镐躲,不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù)侍筛,再執(zhí)行下一個(gè)任務(wù)萤皂。
Objective-C:
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"同步-并發(fā) --- begin");
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行任務(wù)
dispatch_sync(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"同步-并發(fā) --- end");
Swift:
private func text2() {
//打印當(dāng)前線程
NSLog("當(dāng)前線程 --- %@",Thread.current); //打印當(dāng)前線程
NSLog("同步-并發(fā) --- begin");
let queue = dispatch_queue_concurrent_t(label: "gcd.name")
//執(zhí)行任務(wù)
queue.sync {
//追加任務(wù) 1
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("1 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 2
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("2 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 3
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("3 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 4
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("4 --- %@",Thread.current); //打印當(dāng)前線程
}
NSLog("同步-并發(fā) --- end");
}
打印結(jié)果:
2020-08-18 15:31:20.826512+0800 KX_GCD_Demo[27676:531403] 當(dāng)前線程 --- <NSThread: 0x600002db0d40>{number = 1, name = main}
2020-08-18 15:31:20.826660+0800 KX_GCD_Demo[27676:531403] 同步-并發(fā) --- begin
2020-08-18 15:31:22.828083+0800 KX_GCD_Demo[27676:531403] 1 --- <NSThread: 0x600002db0d40>{number = 1, name = main}
2020-08-18 15:31:24.829041+0800 KX_GCD_Demo[27676:531403] 2 --- <NSThread: 0x600002db0d40>{number = 1, name = main}
2020-08-18 15:31:26.830545+0800 KX_GCD_Demo[27676:531403] 3 --- <NSThread: 0x600002db0d40>{number = 1, name = main}
2020-08-18 15:31:28.832078+0800 KX_GCD_Demo[27676:531403] 4 --- <NSThread: 0x600002db0d40>{number = 1, name = main}
2020-08-18 15:31:28.832398+0800 KX_GCD_Demo[27676:531403] 同步-并發(fā) --- end
結(jié)論:
- 所有任務(wù)都是在當(dāng)前線程即主線程中執(zhí)行的,沒有開啟新的線程(同步執(zhí)行不具備開啟新線程的能力)
- 所有任務(wù)都在begin和end之間執(zhí)行的(同步執(zhí)行需要等待隊(duì)列中,前邊的任務(wù)執(zhí)行完畢,再繼續(xù)執(zhí)行)
- 任務(wù)是按順序執(zhí)行.這里特殊說明一下:雖然并發(fā)隊(duì)列可以開啟多個(gè)線程,同時(shí)執(zhí)行多個(gè)任務(wù).但是因?yàn)楸旧聿痪邆溟_線程的能力,只有當(dāng)前這一個(gè)線程(同步執(zhí)行不具備開線程能力).
所以不能并發(fā)執(zhí)行,只能順序執(zhí)行.而且當(dāng)前線程只有等待當(dāng)前隊(duì)列中正在執(zhí)行的任務(wù)執(zhí)行完畢之后,才能繼續(xù)執(zhí)行.
所以任務(wù)只能一個(gè)接著一個(gè)順序執(zhí)行,不能同時(shí)被執(zhí)行.
2.5.3 異步執(zhí)行 + 串行隊(duì)列
- 會(huì)開啟一條新線程,串行執(zhí)行勾笆,執(zhí)行完一個(gè)任務(wù)敌蚜,再執(zhí)行下一個(gè)任務(wù)
Objective-C:
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"異步-串行 --- begin");
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_SERIAL);
//執(zhí)行任務(wù)
dispatch_async(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"異步-串行 --- end");
Swift:
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"異步-串行 --- begin");
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_SERIAL);
//執(zhí)行任務(wù)
dispatch_async(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"異步-串行 --- end");
打印結(jié)果:
2020-08-18 15:58:20.620164+0800 KX_GCD_Demo[27846:545723] 當(dāng)前線程 --- <NSThread: 0x60000269c240>{number = 1, name = main}
2020-08-18 15:58:20.620322+0800 KX_GCD_Demo[27846:545723] 異步-串行 --- begin
2020-08-18 15:58:20.620464+0800 KX_GCD_Demo[27846:545723] 異步-串行 --- end
2020-08-18 15:58:22.621062+0800 KX_GCD_Demo[27846:545800] 1 --- <NSThread: 0x6000026c4180>{number = 7, name = (null)}
2020-08-18 15:58:24.625413+0800 KX_GCD_Demo[27846:545800] 2 --- <NSThread: 0x6000026c4180>{number = 7, name = (null)}
2020-08-18 15:58:26.626524+0800 KX_GCD_Demo[27846:545800] 3 --- <NSThread: 0x6000026c4180>{number = 7, name = (null)}
2020-08-18 15:58:28.629949+0800 KX_GCD_Demo[27846:545800] 4 --- <NSThread: 0x6000026c4180>{number = 7, name = (null)}
結(jié)論:
- 開啟了一條新的線程(異步執(zhí)行具備開線程的能力,串行隊(duì)列只能開啟一個(gè)線程)
- 所有任務(wù)都是在begin和end之后才開始執(zhí)行(異步執(zhí)行不會(huì)做任何等待,可以開線程繼續(xù)執(zhí)行任務(wù))
- 任務(wù)是順序執(zhí)行的(串行隊(duì)列每次只能執(zhí)行一個(gè)任務(wù),任務(wù)是按順序執(zhí)行的)
2.5.4 異步執(zhí)行 + 并發(fā)隊(duì)列
- 可以開啟多個(gè)線程,任務(wù)交替(同時(shí))執(zhí)行窝爪。
OBJECTIVE-C:
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"異步-并發(fā) --- begin");
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行任務(wù)
dispatch_async(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"異步-并發(fā) --- end");
SWIFT:
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"異步-并發(fā) --- begin");
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行任務(wù)
dispatch_async(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"異步-并發(fā) --- end");
打印結(jié)果:
2020-08-18 16:04:42.114241+0800 KX_GCD_Demo[28014:554532] 當(dāng)前線程 --- <NSThread: 0x6000024d0ec0>{number = 1, name = main}
2020-08-18 16:04:42.114406+0800 KX_GCD_Demo[28014:554532] 異步-并發(fā) --- begin
2020-08-18 16:04:42.114557+0800 KX_GCD_Demo[28014:554532] 異步-并發(fā) --- end
2020-08-18 16:04:44.116285+0800 KX_GCD_Demo[28014:554707] 4 --- <NSThread: 0x600002480500>{number = 4, name = (null)}
2020-08-18 16:04:44.116285+0800 KX_GCD_Demo[28014:554706] 2 --- <NSThread: 0x600002495800>{number = 5, name = (null)}
2020-08-18 16:04:44.116285+0800 KX_GCD_Demo[28014:554705] 1 --- <NSThread: 0x600002487880>{number = 8, name = (null)}
2020-08-18 16:04:44.116360+0800 KX_GCD_Demo[28014:554708] 3 --- <NSThread: 0x60000249cd40>{number = 6, name = (null)}
結(jié)論:
- 除了當(dāng)前線程(主線程),系統(tǒng)又開啟4個(gè)新的線程,并且任務(wù)是交替執(zhí)行的(同時(shí)執(zhí)行)弛车。說明異步執(zhí)行具備開線程的能力,并且并發(fā)隊(duì)列可以開啟多個(gè)線程,同時(shí)執(zhí)行多個(gè)任務(wù)齐媒。
- 所有任務(wù)都是在begin和end之后執(zhí)行的,說明當(dāng)前線程沒有等待纷跛,而是直接開啟了新線程,在新線程中執(zhí)行任務(wù)的喻括。
2.5.5 同步執(zhí)行 + 主隊(duì)列(在主線程調(diào)用)
- 在主線程調(diào)用結(jié)果:死鎖
- 在子線程調(diào)用結(jié)果:不會(huì)開啟新線程,執(zhí)行完一個(gè)任務(wù)贫奠,再執(zhí)行下一個(gè)任務(wù)
今天只演示在主線程調(diào)用:
OBJECTIVE-C:
- (void)text5{
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"同步執(zhí)行-主隊(duì)列 --- begin");
dispatch_queue_t queue = dispatch_get_main_queue();
//執(zhí)行任務(wù)
dispatch_sync(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_sync(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"同步執(zhí)行-主隊(duì)列 --- end");
}
Swift:
private func text5() {
//主線程是一個(gè)特殊的線程.相對(duì)于主線程其他線程都叫做子線程.
NSLog("當(dāng)前線程 --- %@",Thread.current);//打印當(dāng)前線程
NSLog("同步執(zhí)行-主隊(duì)列 --- begin");
let queue = DispatchQueue.main
//執(zhí)行任務(wù)
queue.sync {
//追加任務(wù) 1
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("1 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 2
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("2 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 3
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("3 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.sync {
//追加任務(wù) 4
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("4 --- %@",Thread.current); //打印當(dāng)前線程
}
NSLog("同步執(zhí)行-主隊(duì)列 --- end");
}
打印結(jié)果:
2020-08-18 16:27:17.592790+0800 KX_GCD_Demo[28140:565766] 當(dāng)前線程 --- <NSThread: 0x600003954d40>{number = 1, name = main}
2020-08-18 16:27:17.592934+0800 KX_GCD_Demo[28140:565766] 同步執(zhí)行-主隊(duì)列 --- begin
(lldb)
結(jié)論:
同步執(zhí)行 + 主隊(duì)列
結(jié)論:
追加到主線程的任務(wù)都不再執(zhí)行,而是直接報(bào)崩潰了.
這是因?yàn)槲覀冊(cè)谥骶€程中執(zhí)行方法text5
唬血,相當(dāng)于把text5
任務(wù)放到了主線程的隊(duì)列中,而同步執(zhí)行會(huì)等待當(dāng)前隊(duì)列中的任務(wù)執(zhí)行完畢唤崭,才會(huì)繼續(xù)執(zhí)行.那么我們把任務(wù)1
追加到主隊(duì)列中拷恨,任務(wù)1
就在等待主線程把處理完text5
任務(wù),而text5
任務(wù)需要等待任務(wù)1
執(zhí)行完畢才能接著執(zhí)行谢肾。這種情況下腕侄,
text5
和任務(wù)1
都在等對(duì)方執(zhí)行完畢。就這樣大家相互等待,所以就卡主了芦疏。所以我們的任務(wù)就執(zhí)行不了冕杠,
后面的代碼也一直沒有執(zhí)行.
面試經(jīng)常會(huì)問這個(gè)問題。
2.5.6 異步執(zhí)行 + 主隊(duì)列
- 只在主線程中執(zhí)行任務(wù)酸茴,執(zhí)行完一個(gè)任務(wù)分预,再執(zhí)行下一個(gè)任務(wù)。
Objective-C:
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"異步執(zhí)行-主隊(duì)列 --- begin");
dispatch_queue_t queue = dispatch_get_main_queue();
//執(zhí)行任務(wù)
dispatch_async(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
NSLog(@"異步執(zhí)行-主隊(duì)列 --- end");
Swift:
private func text6() {
NSLog("當(dāng)前線程 --- %@",Thread.current);//打印當(dāng)前線程
NSLog("同步執(zhí)行-主隊(duì)列 --- begin");
let queue = DispatchQueue.main
//執(zhí)行任務(wù)
queue.async {
//追加任務(wù) 1
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("1 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.async {
//追加任務(wù) 2
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("2 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.async {
//追加任務(wù) 3
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("3 --- %@",Thread.current); //打印當(dāng)前線程
}
queue.async {
//追加任務(wù) 4
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("4 --- %@",Thread.current); //打印當(dāng)前線程
}
NSLog("異步執(zhí)行-主隊(duì)列 --- end");
}
打印結(jié)果:
2020-08-18 20:40:28.667081+0800 KX_GCD_Demo[41989:696384] 當(dāng)前線程 --- <NSThread: 0x6000015ccac0>{number = 1, name = main}
2020-08-18 20:40:28.667221+0800 KX_GCD_Demo[41989:696384] 異步執(zhí)行-主隊(duì)列 --- begin
2020-08-18 20:40:28.667368+0800 KX_GCD_Demo[41989:696384] 異步執(zhí)行-主隊(duì)列 --- end
2020-08-18 20:40:30.682547+0800 KX_GCD_Demo[41989:696384] 1 --- <NSThread: 0x6000015ccac0>{number = 1, name = main}
2020-08-18 20:40:32.683854+0800 KX_GCD_Demo[41989:696384] 2 --- <NSThread: 0x6000015ccac0>{number = 1, name = main}
2020-08-18 20:40:34.684356+0800 KX_GCD_Demo[41989:696384] 3 --- <NSThread: 0x6000015ccac0>{number = 1, name = main}
2020-08-18 20:40:36.685728+0800 KX_GCD_Demo[41989:696384] 4 --- <NSThread: 0x6000015ccac0>{number = 1, name = main}
結(jié)論:
- 所有任務(wù)都是在當(dāng)前線程執(zhí)行的,并沒有開啟新的線程(雖然異步執(zhí)行具備開啟線程的能力,但是你因?yàn)槭侵麝?duì)列,所以所有任務(wù)都在主線程中)
- 所有任務(wù)是在begin和end之后才執(zhí)行的.(異步不等待,可以繼續(xù)執(zhí)行)
- 任務(wù)是按順序執(zhí)行的,主隊(duì)列其實(shí)也是一個(gè)串行的隊(duì)列
2.6 線程間的通信
線程通信案例:子線程處理完耗時(shí)操作后薪捍,回到主線程刷新UI笼痹,這樣就完成了線程之間的通信。
案例:
Objective-C
//獲取全局并發(fā)隊(duì)列
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//獲取主隊(duì)列
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(queue, ^{
//異步追加任務(wù)1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
//回到主線程
dispatch_async(mainQueue, ^{
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
});
Swift
private func text7() {
//線程間的通信比較好理解,就是兩個(gè)線程之間傳遞信息
//獲取全局并發(fā)隊(duì)列
let queue = DispatchQueue.global()
//獲取主隊(duì)列
let mainQueue = DispatchQueue.main
queue.async {
//異步追加任務(wù)1
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("1 --- %@",Thread.current); //打印當(dāng)前線程
//回到主線程
mainQueue.async {
Thread.sleep(forTimeInterval: 2) //模擬耗時(shí)操作
NSLog("2 --- %@",Thread.current); //打印當(dāng)前線程
}
}
}
2.7GCD中的其他方法
2.7.1柵欄方法
使用情況:第一組(多個(gè))任務(wù)執(zhí)行完成之后酪穿,再進(jìn)行第二組操作与倡。相當(dāng)于在兩組之間添加了一個(gè)柵欄,阻隔開兩組任務(wù)的執(zhí)行昆稿。這里就用到了dispatch_barrier_async
方法纺座。
//柵欄方法
dispatch_queue_t queue = dispatch_queue_create("gcd.name", DISPATCH_QUEUE_CONCURRENT);
//執(zhí)行任務(wù)
dispatch_async(queue, ^{
//追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"1 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"2 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
//柵欄方法
dispatch_barrier_async(queue, ^{
//追加任務(wù)barrier
[NSThread sleepForTimeInterval:2];//模擬耗時(shí)操作
NSLog(@"barrier --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 3
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"3 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
dispatch_async(queue, ^{
//追加任務(wù) 4
[NSThread sleepForTimeInterval:2]; //模擬耗時(shí)操作
NSLog(@"4 --- %@",[NSThread currentThread]); //打印當(dāng)前線程
});
打印結(jié)果:
2020-08-19 11:40:11.637863+0800 KX_GCD_Demo[44619:935467] 2 --- <NSThread: 0x600001cc6d40>{number = 5, name = (null)}
2020-08-19 11:40:11.637871+0800 KX_GCD_Demo[44619:935470] 1 --- <NSThread: 0x600001cc6bc0>{number = 3, name = (null)}
2020-08-19 11:40:13.638746+0800 KX_GCD_Demo[44619:935467] barrier --- <NSThread: 0x600001cc6d40>{number = 5, name = (null)}
2020-08-19 11:40:15.640520+0800 KX_GCD_Demo[44619:935470] 4 --- <NSThread: 0x600001cc6bc0>{number = 3, name = (null)}
2020-08-19 11:40:15.640521+0800 KX_GCD_Demo[44619:935467] 3 --- <NSThread: 0x600001cc6d40>{number = 5, name = (null)}
結(jié)論:在執(zhí)行完柵欄前面的操作之后,才執(zhí)行柵欄操作溉潭,最后再執(zhí)行柵欄后邊的操作净响。
2.7.2 延時(shí)執(zhí)行 dispatch_after
在項(xiàng)目中延時(shí)執(zhí)行有多種方法,dispatch_after就是其中一個(gè)喳瓣。
NSLog(@"當(dāng)前線程 --- %@",[NSThread currentThread]);//打印當(dāng)前線程
NSLog(@"after --- begin");
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"after --- %@",[NSThread currentThread]);//打印當(dāng)前線程
});
打印結(jié)果:
2020-08-19 15:24:52.379680+0800 KX_GCD_Demo[45859:1033251] 當(dāng)前線程 --- <NSThread: 0x600002e1c0c0>{number = 1, name = main}
2020-08-19 15:24:52.379818+0800 KX_GCD_Demo[45859:1033251] after --- begin
2020-08-19 15:24:54.380257+0800 KX_GCD_Demo[45859:1033251] after --- <NSThread: 0x600002e1c0c0>{number = 1, name = main}
2.7.3 單通道執(zhí)行(只執(zhí)行一次):dispatch_once
常用的就是單例的情況了:
static id _instance;
+ (instancetype)shared{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc]init];
});
return _instance;
}
2.7.4 隊(duì)列組:dispatch_group
案例情況:執(zhí)行多個(gè)任務(wù)馋贤,執(zhí)行完成之后再回到主線程去執(zhí)行任務(wù)。
有多種隊(duì)列組的方法可以實(shí)現(xiàn)畏陕。
- 通過
dispatch_group_async
將任務(wù)放入到隊(duì)列中配乓,然后將隊(duì)列放入隊(duì)列組中 - 使用
dispatch_group_enter
和dispatch_group_leave
實(shí)現(xiàn),和dispatch_group_async
功能一樣,只是方法不同而已犹芹。 - 調(diào)用隊(duì)列組的
dispatch_group_notify
回到指定線程執(zhí)行任務(wù)崎页。 - 使用
dispatch_group_wait
回到當(dāng)前線程繼續(xù)向下執(zhí)行(會(huì)阻塞當(dāng)前線程)。
- dispatch_group_notify
監(jiān)聽 group 中任務(wù)的完成狀態(tài)腰埂,當(dāng)所有的任務(wù)都執(zhí)行完成后飒焦,追加任務(wù)到 group 中,并執(zhí)行任務(wù)屿笼。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步任務(wù) 1牺荠、任務(wù) 2 都執(zhí)行完畢后,回到主線程執(zhí)行下邊任務(wù)
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
NSLog(@"group---end");
打印結(jié)果:
2020-08-25 22:24:40.382897+0800 KX_GCD_Demo[1429:33018] currentThread---<NSThread: 0x600001638ec0>{number = 1, name = main}
2020-08-25 22:24:40.383599+0800 KX_GCD_Demo[1429:33018] group---begin
2020-08-25 22:24:40.384450+0800 KX_GCD_Demo[1429:33018] group---end
2020-08-25 22:24:42.387804+0800 KX_GCD_Demo[1429:33234] 2---<NSThread: 0x600001678080>{number = 5, name = (null)}
2020-08-25 22:24:42.387766+0800 KX_GCD_Demo[1429:33237] 1---<NSThread: 0x600001672700>{number = 6, name = (null)}
2020-08-25 22:24:44.389329+0800 KX_GCD_Demo[1429:33018] 3---<NSThread: 0x600001638ec0>{number = 1, name = main}
- dispatch_group_wait
阻塞當(dāng)前線程驴一,等待指定的 group 中所有的任務(wù)執(zhí)行完成后休雌,才會(huì)繼續(xù)往下執(zhí)行。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
// 等待上面的任務(wù)全部完成后肝断,會(huì)往下繼續(xù)執(zhí)行(會(huì)阻塞當(dāng)前線程)
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"group---end");
打印結(jié)果:
2020-08-25 22:28:13.240155+0800 KX_GCD_Demo[1461:35597] currentThread---<NSThread: 0x600001ba8380>{number = 1, name = main}
2020-08-25 22:28:13.240345+0800 KX_GCD_Demo[1461:35597] group---begin
2020-08-25 22:28:15.242472+0800 KX_GCD_Demo[1461:35685] 1---<NSThread: 0x600001becc00>{number = 5, name = (null)}
2020-08-25 22:28:15.242493+0800 KX_GCD_Demo[1461:35687] 2---<NSThread: 0x600001bfcd80>{number = 7, name = (null)}
2020-08-25 22:28:15.242974+0800 KX_GCD_Demo[1461:35597] group---end
注意看打印結(jié)果的時(shí)間挑辆,是阻塞線程之后再執(zhí)行的end
.
- dispatch_group_enter、dispatch_group_leave
這是我最喜歡用的實(shí)現(xiàn)方案孝情。
- dispatch_group_enter 標(biāo)志著一個(gè)任務(wù)追加到 group,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù) +1
- dispatch_group_leave 標(biāo)志著一個(gè)任務(wù)離開了 group洒嗤,相當(dāng)于 group 中未執(zhí)行完畢任務(wù)數(shù) -1箫荡。
- 當(dāng) group 中未執(zhí)行完畢任務(wù)數(shù)為0的時(shí)候,才會(huì)使 dispatch_group_wait 解除阻塞渔隶,以及最后去執(zhí)行-追加到 dispatch_group_notify 中的任務(wù)羔挡。
NSLog(@"currentThread---%@",[NSThread currentThread]); // 打印當(dāng)前線程
NSLog(@"group---begin");
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任務(wù) 1
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@",[NSThread currentThread]); // 打印當(dāng)前線程
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(queue, ^{
// 追加任務(wù) 2
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@",[NSThread currentThread]); // 打印當(dāng)前線程
dispatch_group_leave(group);
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程.
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@",[NSThread currentThread]); // 打印當(dāng)前線程
});
NSLog(@"group---end");
打印結(jié)果:
2020-08-25 22:33:53.365593+0800 KX_GCD_Demo[1511:38898] currentThread---<NSThread: 0x600003128900>{number = 1, name = main}
2020-08-25 22:33:53.365755+0800 KX_GCD_Demo[1511:38898] group---begin
2020-08-25 22:33:53.365962+0800 KX_GCD_Demo[1511:38898] group---end
2020-08-25 22:33:55.368889+0800 KX_GCD_Demo[1511:38963] 2---<NSThread: 0x600003169ec0>{number = 4, name = (null)}
2020-08-25 22:33:55.368890+0800 KX_GCD_Demo[1511:38966] 1---<NSThread: 0x6000031632c0>{number = 7, name = (null)}
2020-08-25 22:33:57.370401+0800 KX_GCD_Demo[1511:38898] 3---<NSThread: 0x600003128900>{number = 1, name = main}
- 信號(hào)量:dispatch_semaphore
在Dispatch Semaphore
中间唉,使用計(jì)數(shù)來完成這個(gè)功能绞灼,計(jì)數(shù)小于 0 時(shí)等待,不可通過呈野。計(jì)數(shù)為 0 或大于 0 時(shí)低矮,計(jì)數(shù)減 1 且不等待,可通過被冒。
Dispatch Semaphore提供了三個(gè)方法:
- dispatch_semaphore_create:創(chuàng)建一個(gè) Semaphore 并初始化信號(hào)的總量
- dispatch_semaphore_signal:發(fā)送一個(gè)信號(hào)军掂,讓信號(hào)總量加 1
- dispatch_semaphore_wait:可以使總信號(hào)量減 1,信號(hào)總量小于 0 時(shí)就會(huì)一直等待(阻塞所在線程)昨悼,否則就可以正常執(zhí)行蝗锥。
這一塊涉及到線程安全,加鎖的問題率触。比較復(fù)雜终议,我在另一篇文章中有詳細(xì)介紹。
3. NSOperation和NSOperationQueue
NSOperation、NSOperationQueue是完全級(jí)別的封裝穴张,完全面向?qū)ο笙噶恰1?GCD 更簡單易用、代碼可讀性也更高陆馁。
執(zhí)行步驟:
- 將要執(zhí)行的任務(wù)封裝到一個(gè) NSOperation 對(duì)象中找颓。
- 將此任務(wù)添加到一個(gè) NSOperationQueue 對(duì)象中。
值得說明的是叮贩,NSOperation 只是一個(gè)抽象類击狮,所以不能封裝任務(wù)。但它有 2 個(gè)子類用于封裝任務(wù)益老。分別是:NSInvocationOperation 和 NSBlockOperation 彪蓬。創(chuàng)建一個(gè) Operation 后,需要調(diào)用 start 方法來啟動(dòng)任務(wù)捺萌,它會(huì)默認(rèn)在當(dāng)前隊(duì)列同步執(zhí)行档冬。當(dāng)然你也可以在中途取消一個(gè)任務(wù),只需要調(diào)用其 cancel 方法即可桃纯。
3.1 子類NSInvocationOperation
- (void)demo {
// NSOperation *op = [[NSOperation alloc]init];
//NSOperation 是抽象類(有定義沒有實(shí)現(xiàn)),你不能直接使用,需要用子類,或者系統(tǒng)已經(jīng)給你提供好了2個(gè)之類 NSInvocationOperation NSBlockOperation
//1.創(chuàng)建NSInvocationOperation對(duì)象
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task) object:nil];
//2.開始執(zhí)行
[operation start];
}
- (void)task{
NSLog(@"task %@",[NSThread currentThread]);
}
在沒有使用NSOperationQueue的情況下酷誓,NSInvocationOperation是在當(dāng)前線程執(zhí)行的操作。
滋埂:Swift語言環(huán)境下盐数,是不支持NSInvocationOperation的
,應(yīng)為Swift覺得NSInvocationOperation
不安全伞梯。
3.2 子類NSBlockOperation
Obejective-C:
- (void)useBlockOperation {
// 1.創(chuàng)建 NSBlockOperation 對(duì)象
NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
// 2.開始執(zhí)行操作
[op start];
}
//------------------------------------------------------------------
Swift:
func useBlockOperation() {
// 1.創(chuàng)建 NSBlockOperation 對(duì)象
let op = BlockOperation {
for _ in 0...2 {
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
NSLog("1---%@", Thread.current) // 打印當(dāng)前線程
}
}
// 2.開始執(zhí)行操作
op.start()
}
操作是在當(dāng)前線程執(zhí)行的玫氢,并沒有開啟新線程。
NSBlockOperation還提供了addExecutionBlock
方法谜诫,追加可執(zhí)行的block漾峡。
例如:
Objective-C:
NSBlockOperation *op1 =[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1 %@",[NSThread currentThread]);
}];
//追加可執(zhí)行的block
[op1 addExecutionBlock:^{
NSLog(@"task2 %@",[NSThread currentThread]);
}];
[op1 start];
//----------------------------------------------------
Swift:
let op1 = BlockOperation {
NSLog("task1 %@",Thread.current)
}
//追加可執(zhí)行的block
op1.addExecutionBlock {
NSLog("task2 %@",Thread.current)
}
op1.start()
3.3創(chuàng)建隊(duì)列
- 自定義隊(duì)列
- 添加到這種隊(duì)列中的操作,就會(huì)自動(dòng)放到子線程中執(zhí)行喻旷。
- 同時(shí)包含了:串行生逸、并發(fā)功能。
Objective-C:
// 主隊(duì)列獲取方法
NSOperationQueue *queue = [NSOperationQueue mainQueue];
//----------------------------------------------------
Swift:
let queue = OperationQueue.main
- 主隊(duì)列
-凡是添加到主隊(duì)列中的操作且预,都會(huì)放到主線程中執(zhí)行(特殊情況除外)
Objective-C:
// 自定義隊(duì)列創(chuàng)建方法
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//----------------------------------------------------------------
Swift:
let queue = OperationQueue()
3.4 將操作添加到隊(duì)列中
3.4.1 使用addOperation
簡單的例子:
NSInvocationOperation *op1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(task) object:nil];
//2.隊(duì)列 并發(fā)
NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//把操作添加到隊(duì)列中
[queue addOperation:op1];
還比如:
Objective-C:
- (void)demo2{
NSBlockOperation *op1 =[NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1 %@",[NSThread currentThread]);
}];
//追加可執(zhí)行的block
[op1 addExecutionBlock:^{
NSLog(@"task2 %@",[NSThread currentThread]);
}];
queue = [[NSOperationQueue alloc]init];
//開了新的線程
[queue addOperation:op1];
}
//--------------------------------------------------------
Swift:
func demo2() {
let op1 = BlockOperation {
NSLog("task1 %@",Thread.current);
}
//追加可執(zhí)行的closur
op1.addExecutionBlock {
NSLog("task2 %@",Thread.current);
}
let queue = OperationQueue()
//開了新的線程
queue.addOperation(op1)
}
開啟了新的線程牺陶。
也可以兩種子類結(jié)合起來用:
/**
* 使用 addOperation: 將操作加入到操作隊(duì)列中
*/
- (void)addOperationToQueue {
// 1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.創(chuàng)建操作
// 使用 NSInvocationOperation 創(chuàng)建操作1
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
// 使用 NSInvocationOperation 創(chuàng)建操作2
NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
// 使用 NSBlockOperation 創(chuàng)建操作3
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
[op3 addExecutionBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
// 3.使用 addOperation: 添加所有操作到隊(duì)列中
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op2]; // [op2 start]
[queue addOperation:op3]; // [op3 start]
NSLog(@"over");
}
- (void)task1{
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
- (void)task2{
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
打印結(jié)果:
2020-08-26 21:52:45.953187+0800 05.NSOperation[1059:29510] over
2020-08-26 21:52:47.954189+0800 05.NSOperation[1059:29738] 1---<NSThread: 0x60000155b280>{number = 6, name = (null)}
2020-08-26 21:52:47.954199+0800 05.NSOperation[1059:29736] 3---<NSThread: 0x600001554000>{number = 5, name = (null)}
2020-08-26 21:52:47.954193+0800 05.NSOperation[1059:29739] 4---<NSThread: 0x600001550180>{number = 2, name = (null)}
2020-08-26 21:52:47.954209+0800 05.NSOperation[1059:29737] 2---<NSThread: 0x600001550680>{number = 4, name = (null)}
2020-08-26 21:52:49.958189+0800 05.NSOperation[1059:29739] 4---<NSThread: 0x600001550180>{number = 2, name = (null)}
2020-08-26 21:52:49.958218+0800 05.NSOperation[1059:29736] 3---<NSThread: 0x600001554000>{number = 5, name = (null)}
結(jié)論:先打印的over
,全部在子線程,并發(fā)執(zhí)行辣之。
使用 NSOperation 子類創(chuàng)建操作掰伸,使用 addOperation:
將操作加入到操作隊(duì)列后有開啟新線程的能力。
另外:操作隊(duì)列添加操作還有一個(gè)方法怀估,可以一次添加多個(gè)操作狮鸭。
Objective-C:
/**
添加多個(gè)操作到隊(duì)列
一個(gè)操作只能夠被添加到一個(gè)隊(duì)列中
*/
- (void)demo3 {
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task1 %@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"task2 %@",[NSThread currentThread]);
[NSThread sleepForTimeInterval:0.5];
}];
// [self.queue addOperation:op1];
// [self.queue addOperation:op2];
[self.queue addOperations:@[op1,op2] waitUntilFinished:NO];
//當(dāng)NO的時(shí)候不阻塞,當(dāng)yes的時(shí)候會(huì)阻塞后面的代碼
NSLog(@"over");
}
//---------------------------------------------------------
Swift:
func demo3() {
let op1 = BlockOperation {
NSLog("task1 %@",Thread.current);
}
let op2 = BlockOperation {
NSLog("task2 %@",Thread.current);
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
}
let queue = OperationQueue()
queue.addOperations([op1,op2], waitUntilFinished: false)
NSLog("over")
}
[self.queue addOperations:@[op1,op2] waitUntilFinished:NO];
當(dāng)NO的時(shí)候不阻塞,當(dāng)yes的時(shí)候會(huì)阻塞后面的代碼
3.4.2 使用addOperationWithBlock
這種方法不需要事先創(chuàng)建操作合搅,直接將操作添加到block中。
Objective-C :
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue addOperationWithBlock:^{
NSLog(@"task-- %@",[NSThread currentThread]);
}];
//------------------------------------------------------
Swift:
let queue = OperationQueue()
queue.addOperation {
NSLog("task-- %@",Thread.current);
}
打印結(jié)果:
2020-08-26 22:03:29.328099+0800 05.NSOperation[1110:35213] task1 <NSThread: 0x600003fe4080>{number = 3, name = (null)}
僅僅添加了一個(gè)方法歧蕉,就已經(jīng)開線程了灾部,說明使用
addOperationWithBlock
將操作加入到操作隊(duì)列后能夠開啟新線程。
那我們多使用幾次addOperationWithBlock
就會(huì)發(fā)現(xiàn)不但可以開線程惯退,而且是并發(fā)執(zhí)行赌髓。
3.5 NSOperationQueue 控制串行執(zhí)行、并發(fā)執(zhí)行
這也是我任務(wù)NSOperationQueue 最厲害的地方催跪,它可以控制開線程的個(gè)數(shù)锁蠕。
關(guān)鍵屬性最大并發(fā)操作數(shù):maxConcurrentOperationCount
,用來控制隊(duì)列懊蒸。
理解為開多少個(gè)線程不夠全面荣倾,但是方便理解。正確解釋為:設(shè)置隊(duì)列中同一時(shí)間能同時(shí)運(yùn)行多少個(gè)類型的任務(wù)操作
骑丸。
引自網(wǎng)絡(luò)博主江蘇小白龍的一條評(píng)論舌仍,接的說的挺有道理,摘抄過來 :認(rèn)為maxConcurrentOperationCount = 1,NSOperationQueue就成了串行隊(duì)列不夠嚴(yán)謹(jǐn)通危,當(dāng)隊(duì)列中有2種類型的任務(wù)操作時(shí)而其中一種任務(wù)類型追加了2個(gè)子類型的任務(wù)操作铸豁,當(dāng)maxConcurrentOperationCount = 1時(shí)只能同時(shí)運(yùn)行一種任務(wù)操作串行運(yùn)行的,當(dāng)他運(yùn)行到有子類型任務(wù)操作的時(shí)候那么他會(huì)開啟新的線程同步執(zhí)行追加的任務(wù)菊碟。所以本人認(rèn)為maxConcurrentOperationCount屬性的作用是:設(shè)置隊(duì)列中同一時(shí)間片能同時(shí)運(yùn)行多少個(gè)類型的任務(wù)操作节芥。
案例:
Objective-C:
/**
* 設(shè)置 MaxConcurrentOperationCount(最大并發(fā)操作數(shù))
*/
- (void)setupMaxConcurrentOperationCountDemo {
// 1.創(chuàng)建隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2.設(shè)置最大并發(fā)操作數(shù)
queue.maxConcurrentOperationCount = 1; // 串行隊(duì)列
// queue.maxConcurrentOperationCount = 2; // 并發(fā)隊(duì)列
// queue.maxConcurrentOperationCount = 4; // 并發(fā)隊(duì)列
// 3.添加操作
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
[queue addOperationWithBlock:^{
for (int i = 0; i < 2; i++) {
[NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
NSLog(@"5---%@", [NSThread currentThread]); // 打印當(dāng)前線程
}
}];
NSLog(@"end");
}
Swift:
func setupMaxConcurrentOperationCountDemo() {
// 1.創(chuàng)建隊(duì)列
let queue = OperationQueue()
// 2.設(shè)置最大并發(fā)操作數(shù)
queue.maxConcurrentOperationCount = 1; // 串行隊(duì)列
// queue.maxConcurrentOperationCount = 2; // 并發(fā)隊(duì)列
// queue.maxConcurrentOperationCount = 4; // 并發(fā)隊(duì)列
//添加操作
queue.addOperation {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
NSLog("1--- %@",Thread.current);// 打印當(dāng)前線程
}
}
queue.addOperation {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
NSLog("2--- %@",Thread.current);// 打印當(dāng)前線程
}
}
queue.addOperation {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
NSLog("3--- %@",Thread.current);// 打印當(dāng)前線程
}
}
queue.addOperation {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
NSLog("4--- %@",Thread.current);// 打印當(dāng)前線程
}
}
queue.addOperation {
for _ in 0..<2 {
Thread.sleep(forTimeInterval: 2) // 模擬耗時(shí)操作
NSLog("5--- %@",Thread.current);// 打印當(dāng)前線程
}
}
NSLog("end");
}
最大并發(fā)操作數(shù)為1 時(shí),打印結(jié)果:
2020-08-26 22:16:17.101511+0800 05.NSOperation[1158:40941] end
2020-08-26 22:16:19.106345+0800 05.NSOperation[1158:40992] 1---<NSThread: 0x600000a2e280>{number = 4, name = (null)}
2020-08-26 22:16:21.112007+0800 05.NSOperation[1158:40992] 1---<NSThread: 0x600000a2e280>{number = 4, name = (null)}
2020-08-26 22:16:23.116690+0800 05.NSOperation[1158:40992] 2---<NSThread: 0x600000a2e280>{number = 4, name = (null)}
2020-08-26 22:16:25.122301+0800 05.NSOperation[1158:40992] 2---<NSThread: 0x600000a2e280>{number = 4, name = (null)}
2020-08-26 22:16:27.123054+0800 05.NSOperation[1158:41041] 3---<NSThread: 0x600000a39080>{number = 7, name = (null)}
2020-08-26 22:16:29.124319+0800 05.NSOperation[1158:41041] 3---<NSThread: 0x600000a39080>{number = 7, name = (null)}
2020-08-26 22:16:31.129149+0800 05.NSOperation[1158:41041] 4---<NSThread: 0x600000a39080>{number = 7, name = (null)}
2020-08-26 22:16:33.133247+0800 05.NSOperation[1158:41041] 4---<NSThread: 0x600000a39080>{number = 7, name = (null)}
2020-08-26 22:16:35.138359+0800 05.NSOperation[1158:41041] 5---<NSThread: 0x600000a39080>{number = 7, name = (null)}
2020-08-26 22:16:37.143127+0800 05.NSOperation[1158:41041] 5---<NSThread: 0x600000a39080>{number = 7, name = (null)}
最大并發(fā)操作數(shù)為2 時(shí)框沟,打印結(jié)果:
2020-08-26 22:19:27.567659+0800 05.NSOperation[1195:42965] end
2020-08-26 22:19:29.571120+0800 05.NSOperation[1195:43066] 1---<NSThread: 0x600002a84c80>{number = 6, name = (null)}
2020-08-26 22:19:29.571145+0800 05.NSOperation[1195:43068] 2---<NSThread: 0x600002a9c0c0>{number = 5, name = (null)}
2020-08-26 22:19:31.576032+0800 05.NSOperation[1195:43068] 2---<NSThread: 0x600002a9c0c0>{number = 5, name = (null)}
2020-08-26 22:19:31.576042+0800 05.NSOperation[1195:43066] 1---<NSThread: 0x600002a84c80>{number = 6, name = (null)}
2020-08-26 22:19:33.577990+0800 05.NSOperation[1195:43066] 4---<NSThread: 0x600002a84c80>{number = 6, name = (null)}
2020-08-26 22:19:33.578004+0800 05.NSOperation[1195:43070] 3---<NSThread: 0x600002a9b380>{number = 3, name = (null)}
2020-08-26 22:19:35.582109+0800 05.NSOperation[1195:43070] 3---<NSThread: 0x600002a9b380>{number = 3, name = (null)}
2020-08-26 22:19:35.582133+0800 05.NSOperation[1195:43066] 4---<NSThread: 0x600002a84c80>{number = 6, name = (null)}
2020-08-26 22:19:37.584611+0800 05.NSOperation[1195:43068] 5---<NSThread: 0x600002a9c0c0>{number = 5, name = (null)}
2020-08-26 22:19:39.588468+0800 05.NSOperation[1195:43068] 5---<NSThread: 0x600002a9c0c0>{number = 5, name = (null)}
最大并發(fā)操作數(shù)為4 時(shí),打印結(jié)果:
2020-08-26 22:20:12.711985+0800 05.NSOperation[1213:43733] end
2020-08-26 22:20:14.715188+0800 05.NSOperation[1213:43826] 1---<NSThread: 0x600000bfd900>{number = 4, name = (null)}
2020-08-26 22:20:14.715204+0800 05.NSOperation[1213:43825] 4---<NSThread: 0x600000bb92c0>{number = 8, name = (null)}
2020-08-26 22:20:14.715239+0800 05.NSOperation[1213:43827] 2---<NSThread: 0x600000bfca80>{number = 6, name = (null)}
2020-08-26 22:20:14.715304+0800 05.NSOperation[1213:43828] 3---<NSThread: 0x600000bb9380>{number = 7, name = (null)}
2020-08-26 22:20:16.717543+0800 05.NSOperation[1213:43827] 2---<NSThread: 0x600000bfca80>{number = 6, name = (null)}
2020-08-26 22:20:16.717550+0800 05.NSOperation[1213:43826] 1---<NSThread: 0x600000bfd900>{number = 4, name = (null)}
2020-08-26 22:20:16.717543+0800 05.NSOperation[1213:43825] 4---<NSThread: 0x600000bb92c0>{number = 8, name = (null)}
2020-08-26 22:20:16.717544+0800 05.NSOperation[1213:43828] 3---<NSThread: 0x600000bb9380>{number = 7, name = (null)}
2020-08-26 22:20:18.721737+0800 05.NSOperation[1213:43827] 5---<NSThread: 0x600000bfca80>{number = 6, name = (null)}
2020-08-26 22:20:20.726078+0800 05.NSOperation[1213:43827] 5---<NSThread: 0x600000bfca80>{number = 6, name = (null)}
結(jié)論:當(dāng)最大并發(fā)操作數(shù)為1時(shí)增炭,操作是按順序串行執(zhí)行的忍燥。當(dāng)最大操作并發(fā)數(shù)為2時(shí),操作是并發(fā)執(zhí)行的隙姿,可以同時(shí)執(zhí)行兩個(gè)操作梅垄。而開啟線程數(shù)量是由系統(tǒng)決定的,不需要我們來管理输玷。當(dāng)最大操作并發(fā)數(shù)為4時(shí)队丝,操作是并發(fā)執(zhí)行的,可以同時(shí)執(zhí)行4個(gè)操作欲鹏。
3.5操作依賴
NSOperation 有一個(gè)非常實(shí)用的功能机久,那就是添加依賴。
比如我們有 4 個(gè)任務(wù):1. 登錄APP赔嚎,2.才能付款買東西膘盖,3.下載我們要的資料胧弛,4. 回到主線程刷新UI。這時(shí)就可以用到依賴了:
Demo:
Objective-C :
- (void)demo {
/**
1.登錄
2.付款
3.下載
4.UI
1,2,3,4 有序執(zhí)行
1,2,3 都是耗時(shí)任務(wù) --> 子線程
4 --> 主線程
串行隊(duì)列,異步執(zhí)行
*/
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"登錄 ,%@",[NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"付款 ,%@",[NSThread currentThread]);
}];
NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"下載 ,%@",[NSThread currentThread]);
}];
NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"UI ,%@",[NSThread currentThread]);
}];
[op2 addDependency:op1];
[op3 addDependency:op2];
[op4 addDependency:op3];
[self.queue addOperations:@[op1,op2,op3] waitUntilFinished:NO];
[[NSOperationQueue mainQueue] addOperation:op4];
}
Swift:
func demo6() {
let op1 = BlockOperation {
NSLog("登錄 ,%@",Thread.current);
}
let op2 = BlockOperation {
NSLog("付款 ,%@",Thread.current);
}
let op3 = BlockOperation {
NSLog("下載 ,%@",Thread.current);
}
let op4 = BlockOperation {
NSLog("UI ,%@",Thread.current);
}
op2.addDependency(op1)
op3.addDependency(op2)
op4.addDependency(op3)
let queue = OperationQueue()
queue.addOperations([op1,op2,op3], waitUntilFinished: false)
OperationQueue.main.addOperation(op4)
}
還有一些其他方法:
NSOperation:
BOOL executing; //判斷任務(wù)是否正在執(zhí)行
BOOL finished; //判斷任務(wù)是否完成
void (^completionBlock)(void); //用來設(shè)置完成后需要執(zhí)行的操作
- (void)cancel; //取消任務(wù)
- (void)waitUntilFinished; //阻塞當(dāng)前線程直到此任務(wù)執(zhí)行完畢
NSOperationQueue
NSUInteger operationCount; //獲取隊(duì)列的任務(wù)數(shù)
- (void)cancelAllOperations; //取消隊(duì)列中所有的任務(wù)
- (void)waitUntilAllOperationsAreFinished; //阻塞當(dāng)前線程直到此隊(duì)列中的所有任務(wù)執(zhí)行完畢
[queue setSuspended:YES]; // 暫停queue
[queue setSuspended:NO]; // 繼續(xù)queue
總結(jié):這大概就是GCD侠畔,NSThread结缚,NSOperation的全部用法了,但是還有一些如線程安全的沒有寫软棺。有空再寫吧红竭。
這里面很多demo參考了幾個(gè)優(yōu)秀博主的代碼。文中有寫出來喘落,除此之外還茵宪,整體結(jié)構(gòu)還參考了伯恩的遺產(chǎn)
的一篇文章。非常感謝揖盘。
后續(xù)會(huì)把文中代碼轉(zhuǎn)換為Swift再列出來眉厨。
喜歡就點(diǎn)個(gè)贊再走吧。