概念
進(jìn)程:一個(gè)正在運(yùn)行的程序叫一個(gè)進(jìn)程,進(jìn)程擁有獨(dú)立運(yùn)行所需的全部資源。
線程:線程是指進(jìn)程內(nèi)的一塊執(zhí)行單元,也是執(zhí)行任務(wù)的基本單位。一個(gè)程序,或者說,一個(gè)進(jìn)程,都會(huì)有一個(gè)或多個(gè)線程,如果只有一個(gè),我們叫它主線程,主線程負(fù)責(zé)用戶能看見的任務(wù),例如:添加控件,刷新頁(yè)面,除了主線程以外,都叫子線程,線程之間是獨(dú)立的,并沒有任何聯(lián)系,子線程一般負(fù)責(zé)用戶之間看不到的任務(wù),例如,加載圖片的過程,下載視頻等
線程要明確的:只要用戶看的見的,或者跟用戶看的見的有關(guān)的,咱們都是用主線程操作,因?yàn)殚_啟子線程操作的時(shí)候,是為了更好的用戶體驗(yàn),用戶體驗(yàn)直接表現(xiàn)為:看到的或者點(diǎn)擊的流暢
線程是耗費(fèi)資源的,雖然多線程操作,可以提高用戶體驗(yàn),但是,不建議,進(jìn)行很多線程同時(shí)進(jìn)行操作
一個(gè)進(jìn)程是由一或多個(gè)線程組成。進(jìn)程只負(fù)責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元,負(fù)責(zé)代碼的執(zhí)行。
與進(jìn)程的區(qū)別:
(1)地址空間:進(jìn)程內(nèi)的一個(gè)執(zhí)行單元;進(jìn)程至少有一個(gè)線程;它們共享進(jìn)程的地址空間;而進(jìn)程有自己獨(dú)立的地址空間。
(2)資源擁有:進(jìn)程是資源分配和擁有的單位,同一個(gè)進(jìn)程內(nèi)的線程共享進(jìn)程的資源。
(3)線程是處理器調(diào)度的基本單位,但進(jìn)程不是。
(4)二者均可并發(fā)執(zhí)行。
線程的主要內(nèi)容:
單線程:每個(gè)正在運(yùn)行的程序(即進(jìn)程),至少包含一個(gè)線程,這個(gè)線程叫主線程。
1.主線程在程序啟動(dòng)時(shí)被創(chuàng)建,用于執(zhí)行 main 函數(shù)。
2.只有一個(gè)主線程的程序,稱作單線程程序。
3.主線程負(fù)責(zé)執(zhí)行程序的所有代碼(UI 展現(xiàn)以及刷新,網(wǎng)絡(luò)請(qǐng)求,本地存儲(chǔ)等等)。這些代碼只能順序執(zhí)行,無(wú)法并發(fā)執(zhí)行。
多線程
1.擁有多個(gè)線程的程序,稱作多線程程序
2.iOS 允許用戶自己開辟新的線程,相對(duì)于主線程來講,這些線程,稱作子線程。
3.可以根據(jù)需要開辟若干子線程
4.子線程和主線程都是獨(dú)立的運(yùn)行單元,各自的執(zhí)行互不影響,因此能夠并發(fā)執(zhí)行。
3、單、多線程區(qū)別
單線程程序:只有一個(gè)線程,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁(yè)面假死)。
多線程程序:有多個(gè)線程,線程間獨(dú)立運(yùn)行,能有效的避免代碼阻塞,并且提高程序的運(yùn)行性能。
注意:iOS 中關(guān)于 UI 的添加和刷新必須在主線程中操作。
iOS 平臺(tái)下的多線程
iOS 多線程實(shí)現(xiàn)種類
- NSThread
- NSOperationQueue
- pthread
- GCD
NSThread
NSThread 是一個(gè)輕量級(jí)的多線程,它有以下兩種創(chuàng)建方法:
- 初始化一個(gè)子線程,但需要手動(dòng)開啟
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
- 初始化一個(gè)子線程并自動(dòng)開啟
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument
- 開啟子線程
start
- 取消當(dāng)前子線程
cancel
- 開啟后臺(tái)執(zhí)行任務(wù)的方法:
[self performSelectorInBackground:@selector(run:) withObject:@"nsobject thread"];
注意:
1、在多線程方法中需要添加自動(dòng)釋放池
2、在應(yīng)用程序打開的時(shí)候,系統(tǒng)會(huì)自動(dòng)為主線程創(chuàng)建一個(gè)自動(dòng)釋放池
3、我們手動(dòng)創(chuàng)建的子程序需要我們手動(dòng)添加自動(dòng)釋放池
// NSThread 輕量級(jí)的線程,可以開啟線程和終止線程
// 創(chuàng)建一個(gè)線程,其實(shí)就是給他一個(gè)方法去執(zhí)行
// 創(chuàng)建一個(gè)子線程,專門用來打印
NSThread *thread = [[NSThread alloc] initWithTarget: self selector:@selector(actionButtonNslog) object:nil];
// 開啟這個(gè)線程
[thread start];
// 線程操作的時(shí)候
// 在主線程的時(shí)候,系統(tǒng)自動(dòng)給咱們添加了一個(gè)自動(dòng)釋放池,那么咱們?cè)匍_啟子線程的時(shí)候,也要添加一個(gè)自動(dòng)釋放池
// 如果你的線程開的比較多,會(huì)造成代碼比較亂,閱讀性不高
// 一般方法中,添加了自動(dòng)釋放池,基本上都是線程方法
NSOperationQueue
NSOperation 也是抽象類 沒有實(shí)現(xiàn)具體功能
NSInvocationOperation 調(diào)用操作(相當(dāng)于任務(wù))
NSBlockOperation 在block 操作
NSOperationQueue 線程隊(duì)列
// 創(chuàng)建任務(wù)一
NSInvocationOperation *invocation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocation1) object:nil];
// 創(chuàng)建任務(wù)二
NSBlockOperation *block2 = [NSBlockOperation blockOperationWithBlock:^{
// block塊中就相當(dāng)于添加的任務(wù)
[self blockOP2];
}];
// 創(chuàng)建一個(gè)隊(duì)列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 注意 添加任務(wù)前要設(shè)置依賴關(guān)系
// 依賴性(串行)
[invocation addDependency:block2];
// 把任務(wù) 添加進(jìn)隊(duì)列當(dāng)中
[queue addOperation:invocation];
[queue addOperation:block2];
// 設(shè)置線程最大并發(fā)數(shù)
queue.maxConcurrentOperationCount = 2;
// 需求 進(jìn)行同步請(qǐng)求一張圖片 不產(chǎn)生屏幕卡頓
// 思路: 開啟一個(gè)子線程 進(jìn)行同步請(qǐng)求圖片 圖片到手后 把圖片返回到主線程 顯示到ImageView上
}
// 實(shí)現(xiàn)任務(wù)一
- (void)invocation1
{
// 添加一個(gè)自動(dòng)釋放池
@autoreleasepool {
// [NSThread currentThread] 當(dāng)前線程的信息
// [NSThread isMainThread] 是否是主線程
// 打印出來額number 是線程的個(gè)數(shù)
NSLog(@"%@ 是不是主線程:%d", [NSThread currentThread], [NSThread isMainThread]);
}
}
// 創(chuàng)建任務(wù)二
- (void)blockOP2
{
// 添加釋放池
@autoreleasepool {
NSLog(@"%@ 是不是主線程:%d", [NSThread currentThread], [NSThread isMainThread]);
}
}
- (void)actionButton
{
if ([self.imageView isAnimating] == NO) {
[self.imageView startAnimating];
} else {
[self.imageView stopAnimating];
}
}
- (void)anotherActionButton
{
// 添加一個(gè)自動(dòng)釋放池
@autoreleasepool {
for (int i = 0; i < 88888; i++) {
NSLog(@"%d", i);
}
}
}
pthread
不經(jīng)常使用
特點(diǎn):
1 一套通用的多線程API
2 適用于Unix\Linux\Windows等系統(tǒng)
3 跨平臺(tái)\可移植
4 使用難度大
使用語(yǔ)言:c語(yǔ)言
GCD
GCD簡(jiǎn)介
- Grand Central Dispatch 簡(jiǎn)稱(CGD),是蘋果公司開發(fā)的技術(shù)。以優(yōu)化應(yīng)用程序支持多核心處理器和其他的對(duì)稱多處理系統(tǒng)的系統(tǒng)。( 對(duì)稱多處理"(Symmetrical Multi-Processing)簡(jiǎn)稱SMP,是指在一個(gè)計(jì)算機(jī)上匯集了一組處理器(多CPU),各CPU之間共享內(nèi)存子系統(tǒng)以及總線結(jié)構(gòu)。它是相對(duì)非對(duì)稱多處理技術(shù)而言的、應(yīng)用十分廣泛的并行技術(shù)。)
- GCD 屬于函數(shù)級(jí)的多線程,性能更高,功能也更加強(qiáng)大。
- 它首次發(fā)布在 Mac OS X 10.6 , iOS4及以上也可以使用。
GCD 核心概念
- 任務(wù):具有一定功能的代碼段。一般是一個(gè) block 或者函數(shù)。
- 分發(fā)隊(duì)列:GCD 以隊(duì)列的方式進(jìn)行工作,F(xiàn)IFO。
- GCD 會(huì)根據(jù)分發(fā)隊(duì)列的類型,創(chuàng)建合適數(shù)量的線程執(zhí)行隊(duì)列中的任務(wù)。
- 主隊(duì)列 是GCD自帶的一種特殊串行隊(duì)列,放在主隊(duì)列中的任務(wù),都會(huì)放在主線程中執(zhí)行
dispatch_get_main_queue() - 全局隊(duì)列 隊(duì)列 任務(wù)執(zhí)行方式 并發(fā)多個(gè)任務(wù)同時(shí)執(zhí)行,串行一個(gè)一個(gè)執(zhí)行
1 串行隊(duì)列 讓任務(wù)一個(gè)接一個(gè)地執(zhí)行 (一個(gè)任務(wù)執(zhí)行完畢后,再執(zhí)行下一個(gè)任務(wù))
2 并發(fā)隊(duì)列 可以多個(gè)任務(wù)同時(shí)執(zhí)行 (自動(dòng)開啟多個(gè)線程同時(shí)執(zhí)行任務(wù))
3 主隊(duì)列 專門負(fù)責(zé)調(diào)度主線程任務(wù),沒有辦法開辟新線程,任務(wù)在主線程只會(huì)順序執(zhí)行
執(zhí)行方式 執(zhí)行的順序 同步按順序執(zhí)行,異步不按順序執(zhí)行
1.同步執(zhí)行 在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
2.異步執(zhí)行 在新的線程中執(zhí)行任務(wù),具備開啟新線程的能力
任務(wù) block
死鎖 如果向主隊(duì)列中添加一個(gè)同步任務(wù)會(huì)死鎖
死鎖原因:我們知道dispatch_sync表示同步的執(zhí)行任務(wù),也就是說執(zhí)行dispatch_sync后,當(dāng)前隊(duì)列會(huì)阻塞。而dispatch_sync中的block如果要在當(dāng)前隊(duì)列中執(zhí)行,就得等待當(dāng)前隊(duì)列程執(zhí)行完成。
主隊(duì)列在執(zhí)行dispatch_sync,隨后隊(duì)列中新增一個(gè)任務(wù)block。因?yàn)橹麝?duì)列是同步隊(duì)列,所以block要等dispatch_sync執(zhí)行完才能執(zhí)行,但是dispatch_sync是同步派發(fā),要等block執(zhí)行完才算是結(jié)束。在主隊(duì)列中的兩個(gè)任務(wù)互相等待,導(dǎo)致了死鎖。
GCD 中兩種隊(duì)列
dispatch queue分為下面兩種:
- SerialQueue:一次只執(zhí)行一個(gè)任務(wù)。Serial queue通常用于同步訪問特定的資源或數(shù)據(jù)。當(dāng)你創(chuàng)建多個(gè)Serial queue時(shí),雖然它們各自是同步執(zhí)行的,但Serial queue與Serial queue之間是并發(fā)執(zhí)行的。Serial queue能實(shí)現(xiàn)線程同步。
- Concurrent:可以并發(fā)的執(zhí)行多個(gè)任務(wù),但是遵守 FIFO。
GCD功能
- dispatch_async( ) 往隊(duì)列中添加任務(wù),任務(wù)會(huì)排隊(duì)執(zhí)行
- dispatch_after( ) 往隊(duì)列中添加任務(wù),任務(wù)不但會(huì)排隊(duì),還會(huì)在延遲的時(shí)間點(diǎn)執(zhí)行
- dispatch_apply( ) 往隊(duì)列中添加任務(wù),任務(wù)會(huì)重復(fù)執(zhí)行 n次
- dispatch_group_async( ) 將任務(wù)添加到隊(duì)列中,并添加分組標(biāo)記
- dispatch_group_notify( ) 將任務(wù)添加到隊(duì)列中,當(dāng)某個(gè)分組的所有任務(wù)執(zhí)行完之后,此任務(wù)才會(huì)執(zhí)行
- dispatch_barrier_async( ) 將任務(wù)添加到隊(duì)列中,此任務(wù)執(zhí)行的時(shí)候,其他任務(wù)停止執(zhí)行
- dispatch_once( ) 任務(wù)添加到隊(duì)列中,但任務(wù)在程序運(yùn)行過程中,只執(zhí)行一次
- dispatch_sync ( ) 將任務(wù)添加到隊(duì)列中,block 不執(zhí)行完,下面代碼不會(huì)執(zhí)行
- dispatch_async_f ( ) 將任務(wù)添加到隊(duì)列中,任務(wù)是函數(shù)非 block
線程間的通信
線程間通信分為兩種:
- 主線程進(jìn)入子線程(前面的方法都可以)
- 子線程回到主線程
返回主線程:
GCD:dispatch_get_main_queue()
NSObject :- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
線程互斥
線程互斥場(chǎng)景:
- 線程互斥是指某一資源同上=時(shí)只允許一個(gè)訪問者對(duì)其進(jìn)行訪問,具有唯一性和排它性。
- 互斥無(wú)法限制訪問者對(duì)資源的訪問順序,即訪問是無(wú)序的。因此需要加上互斥鎖來進(jìn)行順利訪問,最具代表性的就是買票系統(tǒng)。
- NSLock 類能協(xié)助完成互斥操作。
#pragma mark -- 買火車票的線程鎖實(shí)現(xiàn)
- (void)tickets
{
// 初始化票數(shù)
_totalTickets = 100;
// 初始化剩余票數(shù)
_subTickets = 100;
// 初始化線程鎖
self.lock = [[NSLock alloc] init];
// 先創(chuàng)建兩個(gè)并行隊(duì)列
dispatch_queue_t queue1 = dispatch_queue_create("火車站", DISPATCH_QUEUE_CONCURRENT);
// 給火車站,添加賣票的任務(wù)
dispatch_async(queue1, ^{
// 賣票
[self saleTickets:queue1];
});
// 創(chuàng)建12306隊(duì)列
dispatch_queue_t queue2 = dispatch_queue_create("com.12306.www", DISPATCH_QUEUE_CONCURRENT);
dispatch_async(queue2, ^{
// 賣票
[self saleTickets:queue2];
});
}
#pragma mark -- 售票方法
- (void)saleTickets:(dispatch_queue_t)queue
{
// 循環(huán)賣票
while (_subTickets > 0) {
// 添加鎖
// 線程鎖 跟自動(dòng)釋放池 使用的方法差不多
// 中間夾的部分,是鎖的內(nèi)容
[self.lock lock];
// 咱們要知道,是哪個(gè)隊(duì)列來賣票
// 通過隊(duì)列表示符,得到
// dispatch_queue_get_label 得到隊(duì)列的標(biāo)識(shí)符
char *name = (char *) dispatch_queue_get_label(queue);
NSString *str = [NSString stringWithUTF8String:name];
NSLog(@"%@ 還剩%ld張票", str, _subTickets);
// 來一回,減少一張
_subTickets--;
// 鎖結(jié)束
[self.lock unlock];
}
}
group任務(wù)
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue2 = dispatch_queue_create("com.sinosoft.queue",DISPATCH_QUEUE_CONCURRENT);
dispatch_group_async(group, queue2, ^{
for (int i = 0; i<5; i++) {
NSLog(@"1======%d===%@", i, [NSThread currentThread]);
}
});
dispatch_group_async(group, queue2, ^{
for (int i = 0; i<5; i++) {
NSLog(@"2======%d===%@", i, [NSThread currentThread]);
}
});
dispatch_group_notify(group, queue2, ^{
NSLog(@"組1和組2都完成了=========%@", [NSThread currentThread]);
});
long isCompleted = dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"%ld", isCompleted);
/*
dispatch_group_wait方法是一個(gè)很有用的方法,它的完整定義如下:
dispatch_group_wait(group: dispatch_group_t, _ timeout: dispatch_time_t) -> Int
第一個(gè)參數(shù)表示要等待的group,第二個(gè)則表示等待時(shí)間。返回值表示經(jīng)過指定的等待時(shí)間,屬于這個(gè)group的任務(wù)是否已經(jīng)全部執(zhí)行完,如果是則返回0,否則返回非0。
第二個(gè)dispatch_time_t類型的參數(shù)還有兩個(gè)特殊值:DISPATCH_TIME_NOW和DISPATCH_TIME_FOREVER。
前者表示立刻檢查屬于這個(gè)group的任務(wù)是否已經(jīng)完成,后者則表示一直等到屬于這個(gè)group的任務(wù)全部完成。
*/
}