一.多線程
進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個應(yīng)用程序。每個進(jìn)程之間是獨(dú)立的演熟,每個進(jìn)程均運(yùn)行在其專用且受保護(hù)的內(nèi)存空間內(nèi)鞭执。(比如同時打開迅雷、Xcode芒粹,系統(tǒng)就會分別啟動2個進(jìn)程兄纺。)
線程是進(jìn)程的基本執(zhí)行單元,1個進(jìn)程要想執(zhí)行任務(wù)化漆,必須得有線程(每1個進(jìn)程至少要有1條線程,并且1個線程中任務(wù)的執(zhí)行是串行的)估脆。
多線程是指1個進(jìn)程中可以開啟多條線程,多條線程可以并行(同時)執(zhí)行不同的任務(wù)座云。
1.線程的并行:
并行即同時執(zhí)行疙赠。比如同時開啟3條線程分別下載3個文件(分別是文件A付材、文件B、文件C)
2.多線程并發(fā)執(zhí)行的原理:
a. 在同一時間里圃阳,CPU只能處理 1 條線程厌衔,只有 1 條線程在工作(執(zhí)行)
b. 多線程并發(fā)(同時)執(zhí)行,其實是CPU快速地在多條線程之間調(diào)度(切換)捍岳,如果CPU調(diào)度線程的時間足夠快富寿,就造成了多線程并發(fā)執(zhí)行的假象
3.優(yōu)缺點(diǎn)
3.1優(yōu)點(diǎn)
a. 能適當(dāng)提高程序的執(zhí)行效率
b. 能適當(dāng)提高資源利用率(CPU、內(nèi)存利用率)
3.2缺點(diǎn)
a. 開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下锣夹,主線程占用1M页徐,子線程占用512KB),如果開啟大量的線程银萍,會占用大量的內(nèi)存空間变勇,降低程序的性能
b. 線程越多,CPU在調(diào)度線程上的開銷就越大
c. 程序設(shè)計更加復(fù)雜:比如線程之間的通信贴唇、多線程的數(shù)據(jù)共享
4.多線程在ios中的應(yīng)用
4.1主線程
a. 一個iOS程序運(yùn)行后搀绣,默認(rèn)會開啟 1 條線程,稱為“主線程”或“UI 線程”
b. 作用:刷新顯示 UI滤蝠,處理 UI 事件
4.2子線程
a. 除了主線程之外的所有線程豌熄,也叫做后臺線程
b. 子線程不能用來刷新 UI
注意:
a. 不要將耗時操作放到主線程中去處理,因為會卡住線程,造成畫面卡頓的現(xiàn)象
b. 和UI相關(guān)的刷新操作必須放到主線程中進(jìn)行處理
5.多線程安全隱患
- 資源共享
1塊資源可能會被多個線程共享物咳,也就是多個線程可能會訪問同一塊資源
比如多個線程訪問同一個對象、同一個變量蹄皱、同一個文件
- 當(dāng)多個線程訪問同一塊資源時览闰,很容易引發(fā)數(shù)據(jù)錯亂和數(shù)據(jù)安全問題
安全隱患解決– 互斥鎖
- 互斥鎖使用格式
// 注意:鎖定1份代碼只用1把鎖,用多把鎖是無效的
@synchronized(鎖對象){ //需要鎖定的代碼 }
- 互斥鎖的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):能有效防止因多線程搶奪資源造成的數(shù)據(jù)安全問題
缺點(diǎn):需要消耗大量的CPU資源
- 互斥鎖的使用前提:
多條線程搶奪同一塊資源
- 線程同步: 多條線程在同一條線上執(zhí)行(按順序地執(zhí)行任務(wù))
互斥鎖巷折,就是使用了線程同步技術(shù)
6.線程間通信
1個線程傳遞數(shù)據(jù)給另1個線程
在1個線程中執(zhí)行完特定任務(wù)后压鉴,轉(zhuǎn)到另1個線程繼續(xù)執(zhí)行任務(wù)
二.ios中常用多線程方案
-
NSThread
1.創(chuàng)建方式
//1.實例方法
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(loadImage) object:nil];
[thread start];
//2.類方法
[NSThread detachNewThreadSelector:@selector(loadImage) toTarget:self withObject:nil];
//3.//隱式創(chuàng)建線程
[self performSelectorInBackground:@selector(loadImage) withObject:self];
2.當(dāng)前線程獲取
NSThread *current = [NSThread currentThread];
3.獲取主線程
NSThread *main = [NSThread mainThread];
//判斷是否為主線程
-(BOOL)isMainThread;
+(BOOL)isMainThread;
4.暫停當(dāng)前線程
[NSThread sleepForTimeInterval:2];
5.強(qiáng)制停止
+(void)exit;
6.線程之間通信
//在指定線程上執(zhí)行操作
[self performSelector:@selector(run) onThread:thread withObject:nil waitUntilDone:YES];
//在主線程上執(zhí)行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES];
//在當(dāng)前線程執(zhí)行操作
[self performSelector:@selector(run) withObject:nil];
7.簡單使用
//創(chuàng)建線程
-(void)implicitCreateThread{
[self performSelectorInBackground:@selector(loadImageSource:) withObject:imgUrl];
}
//下載圖片數(shù)據(jù)
-(void)loadImageSource:(NSString *)url{
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
UIImage *image = [UIImage imageWithData:imgData];
if (imgData!= nil) {
//主線程中跟新界面
[self performSelectorOnMainThread:@selector(refreshImageView:) withObject:image waitUntilDone:YES];
}else{
NSLog(@"NoData");
}
}
//跟新界面
-(void)refreshImageView:(UIImage *)image{
[self.imageView setImage:image];
}
-
GCD
一.GCD中主要核心概念為任務(wù)和隊列
- 任務(wù):你在線程中執(zhí)行的那段代碼。分為以下兩種.
同步執(zhí)行(sync):只能在當(dāng)前線程中執(zhí)行任務(wù)锻拘,不具備開啟新線程的能力
異步執(zhí)行(async):可以在新的線程中執(zhí)行任務(wù)油吭,具備開啟新線程的能力
- 隊列:用來存放任務(wù)的隊。采用FIFO(先進(jìn)先出)的原則署拟,即新任務(wù)總是被插入到隊列的末尾婉宰,而讀取任務(wù)的時候總是從隊列的頭部開始讀取。分為以下兩種.
并發(fā)隊列(Concurrent Dispatch Queue):可以讓多個任務(wù)并發(fā)(同時)執(zhí)行(自動開啟多個線程同時執(zhí)行任務(wù))
并發(fā)功能只有在異步(dispatch_async)函數(shù)下才有效
串行隊列(Serial Dispatch Queue):讓任務(wù)一個接著一個地執(zhí)行(一個任務(wù)執(zhí)行完畢后推穷,再執(zhí)行下一個任務(wù))
二.GCD創(chuàng)建
GCD創(chuàng)建分為兩步
1.創(chuàng)建一個隊列(串行隊列或并發(fā)隊列)
2.將任務(wù)添加到隊列中心包,然后系統(tǒng)就會根據(jù)任務(wù)類型執(zhí)行任務(wù)(同步執(zhí)行或異步執(zhí)行)
下邊來看看隊列的創(chuàng)建方法和任務(wù)的創(chuàng)建方法。
1.隊列的創(chuàng)建方法
// 串行隊列的創(chuàng)建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
// 并發(fā)隊列的創(chuàng)建方法
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
//主隊列,GCD自帶的一種特殊的串行隊列
dispatch_queue_t queue = dispatch_get_main_queue();
對于并發(fā)隊列馒铃,還可以使用dispatch_get_global_queue來創(chuàng)建全局并發(fā)隊列蟹腾。GCD默認(rèn)提供了全局的并發(fā)隊列痕惋,需要傳入兩個參數(shù)。第一個參數(shù)表示隊列優(yōu)先級娃殖,一般用DISPATCH_QUEUE_PRIORITY_DEFAULT值戳。第二個參數(shù)暫時沒用,用0即可炉爆。
2.任務(wù)的創(chuàng)建方法
// 同步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_sync(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 這里放任務(wù)代碼
});
// 異步執(zhí)行任務(wù)創(chuàng)建方法
dispatch_async(queue, ^{
NSLog(@"%@",[NSThread currentThread]); // 這里放任務(wù)代碼
});
這里根據(jù)隊列和任務(wù)的不同共有6種組合方式堕虹。
并發(fā)隊列 | 串行隊列 | 主隊列 | |
---|---|---|---|
同步(sync) | 沒有開啟新線程,串行執(zhí)行任務(wù) | 沒有開啟新線程叶洞,串行執(zhí)行任務(wù) | 沒有開啟新線程鲫凶,串行執(zhí)行任務(wù) |
異步(async) | 有開啟新線程,并發(fā)執(zhí)行任務(wù) | 有開啟新線程(1條)衩辟,串行執(zhí)行任務(wù) | 沒有開啟新線程螟炫,串行執(zhí)行任務(wù) |
- 并發(fā)隊列 + 同步執(zhí)行
不會開啟新線程,執(zhí)行完一個任務(wù)艺晴,再執(zhí)行下一個任務(wù)
- (void) syncConcurrent
{
NSLog(@"syncConcurrent---begin");
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"syncConcurrent---end");
}
輸出結(jié)果:
2016-09-03 19:22:27.577 GCD[11557:1897538] syncConcurrent---begin
2016-09-03 19:22:27.578 GCD[11557:1897538] 1------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.578 GCD[11557:1897538] 1------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.578 GCD[11557:1897538] 2------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 2------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 3------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] 3------<NSThread: 0x7f82a1d058b0>{number = 1, name = main}
2016-09-03 19:22:27.579 GCD[11557:1897538] syncConcurrent---end
1.從并發(fā)隊列 + 同步執(zhí)行中可以看到昼钻,所有任務(wù)都是在主線程中執(zhí)行的。由于只有一個線程封寞,所以任務(wù)只能一個一個執(zhí)行然评。
2.同時我們還可以看到,所有任務(wù)都在打印的syncConcurrent-begin和syncConcurrent---end之間狈究,這說明任務(wù)是添加到隊列中馬上執(zhí)行的碗淌。
- 并發(fā)隊列 + 異步執(zhí)行
可同時開啟多線程,任務(wù)交替執(zhí)行
- (void) asyncConcurrent
{
NSLog(@"asyncConcurrent---begin");
dispatch_queue_t queue= dispatch_queue_create("test.queue", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"asyncConcurrent---end");
}
輸出結(jié)果:
2016-09-03 19:27:31.503 GCD[11595:1901548] asyncConcurrent---begin
2016-09-03 19:27:31.504 GCD[11595:1901548] asyncConcurrent---end
2016-09-03 19:27:31.504 GCD[11595:1901626] 1------<NSThread: 0x7f8309c22080>{number = 2, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901625] 2------<NSThread: 0x7f8309f0b790>{number = 4, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901855] 3------<NSThread: 0x7f8309e1a950>{number = 3, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901626] 1------<NSThread: 0x7f8309c22080>{number = 2, name = (null)}
2016-09-03 19:27:31.504 GCD[11595:1901625] 2------<NSThread: 0x7f8309f0b790>{number = 4, name = (null)}
2016-09-03 19:27:31.505 GCD[11595:1901855] 3------<NSThread: 0x7f8309e1a950>{number = 3, name = (null)}
1.在并發(fā)隊列 + 異步執(zhí)行中可以看出抖锥,除了主線程亿眠,又開啟了3個線程,并且任務(wù)是交替著同時執(zhí)行的磅废。
2.另一方面可以看出纳像,所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的。說明任務(wù)不是馬上執(zhí)行拯勉,而是將所有任務(wù)添加到隊列之后才開始異步執(zhí)行竟趾。
- 串行隊列 + 同步執(zhí)行
不會開啟新線程,在當(dāng)前線程執(zhí)行任務(wù)宫峦。任務(wù)是串行的岔帽,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)
- (void) syncSerial
{
NSLog(@"syncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_sync(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"syncSerial---end");
}
輸出結(jié)果為:
2016-09-03 19:29:00.066 GCD[11622:1903904] syncSerial---begin
2016-09-03 19:29:00.067 GCD[11622:1903904] 1------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 1------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 2------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 2------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.067 GCD[11622:1903904] 3------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.068 GCD[11622:1903904] 3------<NSThread: 0x7fa2e9f00980>{number = 1, name = main}
2016-09-03 19:29:00.068 GCD[11622:1903904] syncSerial---end
1.在串行隊列 + 同步執(zhí)行可以看到斗遏,所有任務(wù)都是在主線程中執(zhí)行的山卦,并沒有開啟新的線程。而且由于串行隊列,所以按順序一個一個執(zhí)行账蓉。
2.同時我們還可以看到枚碗,所有任務(wù)都在打印的syncConcurrent---begin和syncConcurrent---end之間,這說明任務(wù)是添加到隊列中馬上執(zhí)行的铸本。
- 串行隊列 + 異步執(zhí)行
會開啟新線程肮雨,但是因為任務(wù)是串行的,執(zhí)行完一個任務(wù)箱玷,再執(zhí)行下一個任務(wù)
- (void) asyncSerial
{
NSLog(@"asyncSerial---begin");
dispatch_queue_t queue = dispatch_queue_create("test.queue", DISPATCH_QUEUE_SERIAL);
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"1------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"2------%@",[NSThread currentThread]);
}
});
dispatch_async(queue, ^{
for (int i = 0; i < 2; ++i) {
NSLog(@"3------%@",[NSThread currentThread]);
}
});
NSLog(@"asyncSerial---end");
}
輸出結(jié)果為:
2016-09-03 19:30:08.363 GCD[11648:1905817] asyncSerial---begin
2016-09-03 19:30:08.364 GCD[11648:1905817] asyncSerial---end
2016-09-03 19:30:08.364 GCD[11648:1905895] 1------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 1------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 2------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.364 GCD[11648:1905895] 2------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.365 GCD[11648:1905895] 3------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
2016-09-03 19:30:08.365 GCD[11648:1905895] 3------<NSThread: 0x7fb548c0e390>{number = 2, name = (null)}
1.在串行隊列 + 異步執(zhí)行可以看到怨规,開啟了一條新線程,但是任務(wù)還是串行锡足,所以任務(wù)是一個一個執(zhí)行波丰。
2.另一方面可以看出,所有任務(wù)是在打印的syncConcurrent---begin和syncConcurrent---end之后才開始執(zhí)行的舶得。說明任務(wù)不是馬上執(zhí)行掰烟,而是將所有任務(wù)添加到隊列之后才開始同步執(zhí)行。
三.GCD之間的通訊
在iOS開發(fā)過程中沐批,我們一般在主線程里邊進(jìn)行UI刷新纫骑,例如:點(diǎn)擊、滾動九孩、拖拽等事件先馆。我們通常把一些耗時的操作放在其他線程,比如說圖片下載躺彬、文件上傳等耗時操作煤墙。而當(dāng)我們有時候在其他線程完成了耗時操作時,需要回到主線程宪拥,那么就用到了線程之間的通訊番捂。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSString * url = @"";
NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:url]];
UIImage *image = [UIImage imageWithData:imgData];
// 回到主線程
dispatch_async(dispatch_get_main_queue(), ^{
[self.imageView setImage:image];
});
});
4.GCD 其他用法
- GCD的柵欄方法 dispatch_barrier_async
我們有時需要異步執(zhí)行兩組操作,而且第一組操作執(zhí)行完之后江解,才能開始執(zhí)行第二組操作。這樣我們就需要一個相當(dāng)于柵欄一樣的一個方法將兩組異步執(zhí)行的操作組給分割起來徙歼,當(dāng)然這里的操作組里可以包含一個或多個任務(wù)犁河。這就需要用到dispatch_barrier_async方法在兩個操作組間形成柵欄。
- (void)barrier
{
dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue, ^{
NSLog(@"----1-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----2-----%@", [NSThread currentThread]);
});
dispatch_barrier_async(queue, ^{
NSLog(@"----barrier-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----3-----%@", [NSThread currentThread]);
});
dispatch_async(queue, ^{
NSLog(@"----4-----%@", [NSThread currentThread]);
});
}
輸出結(jié)果:
2016-09-03 19:35:51.271 GCD[11750:1914724] ----1-----<NSThread: 0x7fb1826047b0>{number = 2, name = (null)}
2016-09-03 19:35:51.272 GCD[11750:1914722] ----2-----<NSThread: 0x7fb182423fd0>{number = 3, name = (null)}
2016-09-03 19:35:51.272 GCD[11750:1914722] ----barrier-----<NSThread: 0x7fb182423fd0>{number = 3, name = (null)}
2016-09-03 19:35:51.273 GCD[11750:1914722] ----3-----<NSThread: 0x7fb182423fd0>{number = 3, name = (null)}
2016-09-03 19:35:51.273 GCD[11750:1914724] ----4-----<NSThread: 0x7fb1826047b0>{number = 2, name = (null)}
可以看出在執(zhí)行完柵欄前面的操作之后魄梯,才執(zhí)行柵欄操作桨螺,最后再執(zhí)行柵欄后邊的操作。
- GCD的延時執(zhí)行方法 dispatch_after
當(dāng)我們需要延遲執(zhí)行一段代碼時酿秸,就需要用到GCD的dispatch_after方法灭翔。
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
// 2秒后異步執(zhí)行這里的代碼...
NSLog(@"run-----");
});
- GCD的一次性代碼(只執(zhí)行一次) dispatch_once
我們在創(chuàng)建單例、或者有整個程序運(yùn)行過程中只執(zhí)行一次的代碼時辣苏,我們就用到了GCD的dispatch_once方法肝箱。使用dispatch_once函數(shù)能保證某段代碼在程序運(yùn)行過程中只被執(zhí)行1次哄褒。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執(zhí)行1次的代碼(這里面默認(rèn)是線程安全的)
});
- GCD的隊列組 dispatch_group
有時候我們會有這樣的需求:分別異步執(zhí)行2個耗時操作,然后當(dāng)2個耗時操作都執(zhí)行完畢后再回到主線程執(zhí)行操作煌张。這時候我們可以用到GCD的隊列組呐赡。
我們可以先把任務(wù)放到隊列中,然后將隊列放入隊列組中骏融。
調(diào)用隊列組的dispatch_group_notify回到主線程執(zhí)行操作链嘀。
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 執(zhí)行1個耗時的異步操作
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 等前面的異步操作都執(zhí)行完畢后,回到主線程...
});
-
NSOperation
1.簡介
- NSOperation 是對 GCD 的一層封裝档玻,更加面向?qū)ο蠡巢础V饕蒒SOperation(封裝操作) 和 NSOperationQueue (隊列)實現(xiàn)多線程。
2 .NSOperation
- NSOperation是一個抽象類误趴,不能夠封裝操作霹琼,只能通過以下三種方式來實現(xiàn)封裝操作:
- NSInvocationOperation
- NSBlockOperation
- 自定義子類繼承 NSOperation ,實現(xiàn)內(nèi)部相應(yīng)的方法
- NSInvocationOperation
//創(chuàng)建NSInvocationOperation對象
NSInvocationOperation *invoOperation = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(doSomeThing) object:nil];
//調(diào)用start方法來執(zhí)行操作
[invoOperation start];
NSInvocationOperation比較簡單冤留,繼承了NSOperation碧囊,它是基于一個對象和selector來創(chuàng)建操作,可以直接使用而不需繼承來實現(xiàn)自己的操作處理纤怒。并且默認(rèn)情況下糯而,調(diào)用了start方法之后不會開一條新線程去操作,是在當(dāng)前線程下同步執(zhí)行操作泊窘,只有將其放到NSOperationQueue中才會執(zhí)行異步操作熄驼。
- NSBlockOperation
//創(chuàng)建NSBlockOperation對象
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"create1----%@",[NSThread currentThread]);
}];
//通過addExecutionBlock:方法添加更多的操作
[blockOperation1 addExecutionBlock:^{
NSLog(@"add1-----%@",[NSThread currentThread]);
}];
[blockOperation1 addExecutionBlock:^{
NSLog(@"add2-----%@",[NSThread currentThread]);
}];
[blockOperation1 start];
可以看出NSBlockOperation如果封裝了多個操作,那么除了第一個操作外烘豹,其他的操作會在子線程中進(jìn)行瓜贾。如果只封裝了一個操作,默認(rèn)在主線程中進(jìn)行携悯,并且只要NSBlockOperation封裝的操作數(shù)大于一個祭芦,就會異步執(zhí)行操作。
- 定義繼承自NSOperation的子類
//繼承NSOpetaion
#import "BHOperation.h"
@implementation BHOperation
- (void)main{
NSLog(@"%@",[NSThread currentThread]);
}
@end
BHOperation *operation = [[BHOperation alloc]init];
[operation start];
3.NSOperationQueue
- 主隊列
- 凡是添加到主隊列中的任務(wù)(NSOperation)憔鬼,都會放到主線程中執(zhí)行
NSOperationQueue *queue = [NSOperationQueue mainQueue];
- 其他隊列
- 添加到這種隊列中的任務(wù)(NSOperation)龟劲,就會自動放到子線程中執(zhí)行
- 同時包含了:串行、并發(fā)功能
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
4.將任務(wù)將入到隊列中
1.需要先創(chuàng)建任務(wù)轴或,再將創(chuàng)建好的任務(wù)加入到創(chuàng)建好的隊列中去
- (void)addOperationToQueue
{
// 1.創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2. 創(chuàng)建操作
// 創(chuàng)建NSInvocationOperation
NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];
// 創(chuàng)建NSBlockOperation
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^
NSLog(@"op1-----%@", [NSThread currentThread]);
}];
// 3. 添加操作到隊列中:addOperation:
[queue addOperation:op1]; // [op1 start]
[queue addOperation:op2]; // [op2 start]
}
- (void)run
{
NSLog(@"op2-----%@", [NSThread currentThread]);
}
2.無需先創(chuàng)建任務(wù)昌跌,在block中添加任務(wù),直接將任務(wù)block加入到隊列中照雁。
- (void)addOperationWithBlockToQueue
{
// 1. 創(chuàng)建隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 2. 添加操作到隊列中:addOperationWithBlock:
[queue addOperationWithBlock:^{
NSLog(@"-----%@", [NSThread currentThread]);
}];
}
5. 控制串行執(zhí)行和并行執(zhí)行的關(guān)鍵
這里有個關(guān)鍵參數(shù)maxConcurrentOperationCount蚕愤,叫做最大并發(fā)數(shù)。
最大并發(fā)數(shù):maxConcurrentOperationCount
- maxConcurrentOperationCount默認(rèn)情況下為-1,表示不進(jìn)行限制萍诱,默認(rèn)為并發(fā)執(zhí)行悬嗓。
- 當(dāng)maxConcurrentOperationCount為1時,進(jìn)行串行執(zhí)行砂沛。
- 當(dāng)maxConcurrentOperationCount大于1時烫扼,進(jìn)行并發(fā)執(zhí)行,當(dāng)然這個值不應(yīng)超過系統(tǒng)限制碍庵,即使自己設(shè)置一個很大的值映企,系統(tǒng)也會自動調(diào)整。
6.執(zhí)行順序
對于添加到queue中的操作静浴,它的執(zhí)行順序取決于兩點(diǎn):
1.首先是NSOperation是否準(zhǔn)備好堰氓,是否準(zhǔn)備好是由對象的依賴關(guān)系來決定。
- 操作依賴
NSOperation之間可以設(shè)置依賴來保證執(zhí)行順序苹享,?如一定要讓操作A執(zhí)行完后,才能執(zhí)行操作B
- (void)addDependency
{
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"1-----%@", [NSThread currentThread]);
}];
NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"2-----%@", [NSThread currentThread]);
}];
[op2 addDependency:op1]; // 讓op2 依賴于 op1双絮,則先執(zhí)行op1,在執(zhí)行op2
[queue addOperation:op1];
[queue addOperation:op2];
}
2.根據(jù)NSOperation的相對優(yōu)先級來決定得问,默認(rèn)都是普通的優(yōu)先級囤攀,可以通過setQueuePriority來設(shè)置優(yōu)先級。優(yōu)先級不能替代依賴關(guān)系宫纬,優(yōu)先級是針對于已經(jīng)準(zhǔn)備好的NSOperation來確定執(zhí)行順序焚挠,先滿足依賴關(guān)系,再根據(jù)優(yōu)先級從所有準(zhǔn)備好的操作中選擇執(zhí)行優(yōu)先級最高的那個漓骚。
- 設(shè)置優(yōu)先級
//創(chuàng)建任務(wù)
NSBlockOperation *blockOperation1 = [NSBlockOperation blockOperationWithBlock:^{
NSLog(@"create1----%@",[NSThread currentThread]);
}];
//設(shè)置優(yōu)先級
[blockOperation4 setQueuePriority:NSOperationQueuePriorityVeryHigh];
//加入隊列
[myQueue addOperation:blockOperation1];
//優(yōu)先級:
typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
NSOperationQueuePriorityVeryLow = -8L,
NSOperationQueuePriorityLow = -4L,
NSOperationQueuePriorityNormal = 0,
NSOperationQueuePriorityHigh = 4,
NSOperationQueuePriorityVeryHigh = 8
};
- (注意)先滿足依賴關(guān)系蝌衔,才根據(jù)優(yōu)先級從所有準(zhǔn)備好的操作中選擇優(yōu)先級高的執(zhí)行
7.其他
-(void)cancel;NSOperation提供的方法,可取消單個操作
-(void)cancelAllOperations;NSOperationQueue提供的方法蝌蹂,可以取消隊列的所有操作
-(void)setSuspended:(BOOL)b;可設(shè)置任務(wù)的暫停和恢復(fù)噩斟,YES代表暫停隊列,NO代表恢復(fù)隊列
-(BOOL)isSuspended;判斷暫停狀態(tài)
GCD,NSOpeation,NSThread優(yōu)缺點(diǎn)
NSThread
每個NSThread對象對應(yīng)一個線程孤个,真正最原始的線程剃允。優(yōu)點(diǎn):NSThread 輕量級最低,相對簡單齐鲤。
缺點(diǎn):手動管理所有的線程活動硅急,如生命周期、線程同步佳遂、睡眠等。
NSOperation
自帶線程管理的抽象類撒顿。優(yōu)點(diǎn):自帶線程周期管理丑罪,操作上可更注重自己邏輯。
缺點(diǎn):面向?qū)ο蟮某橄箢悾荒軐崿F(xiàn)它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation吩屹。
GCD
Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的解決方法跪另。優(yōu)點(diǎn):最高效,避開并發(fā)陷阱煤搜。
缺點(diǎn):基于C實現(xiàn)免绿。
選擇小結(jié)
簡單而安全的選擇NSOperation實現(xiàn)多線程即可。
處理大量并發(fā)數(shù)據(jù)擦盾,又追求性能效率的選擇GCD嘲驾。
NSThread本人選擇基本上是在做些小測試上使用,當(dāng)然也可以基于此造個輪子迹卢。