概念
進程:一個正在運行的程序叫一個進程刑峡,進程擁有獨立運行所需的全部資源。
線程:線程是指進程內(nèi)的一塊執(zhí)行單元,也是執(zhí)行任務(wù)的基本單位。一個程序,或者說,一個進程,都會有一個或多個線程,如果只有一個,我們叫它主線程,主線程負責(zé)用戶能看見的任務(wù),例如:添加控件,刷新頁面,除了主線程以外,都叫子線程,線程之間是獨立的,并沒有任何聯(lián)系,子線程一般負責(zé)用戶之間看不到的任務(wù),例如,加載圖片的過程,下載視頻等
線程要明確的:只要用戶看的見的,或者跟用戶看的見的有關(guān)的,咱們都是用主線程操作,因為開啟子線程操作的時候,是為了更好的用戶體驗,用戶體驗直接表現(xiàn)為:看到的或者點擊的流暢
線程是耗費資源的,雖然多線程操作,可以提高用戶體驗,但是,不建議,進行很多線程同時進行操作
一個進程是由一或多個線程組成。進程只負責(zé)資源的調(diào)度和分配,線程才是程序真正的執(zhí)行單元签则,負責(zé)代碼的執(zhí)行。
與進程的區(qū)別:
(1)地址空間:進程內(nèi)的一個執(zhí)行單元铐料;進程至少有一個線程渐裂;它們共享進程的地址空間;而進程有自己獨立的地址空間。
(2)資源擁有:進程是資源分配和擁有的單位钠惩,同一個進程內(nèi)的線程共享進程的資源柒凉。
(3)線程是處理器調(diào)度的基本單位,但進程不是篓跛。
(4)二者均可并發(fā)執(zhí)行膝捞。
線程的主要內(nèi)容:
單線程:每個正在運行的程序(即進程),至少包含一個線程愧沟,這個線程叫主線程蔬咬。
1.主線程在程序啟動時被創(chuàng)建,用于執(zhí)行 main 函數(shù)沐寺。
2.只有一個主線程的程序林艘,稱作單線程程序。
3.主線程負責(zé)執(zhí)行程序的所有代碼(UI 展現(xiàn)以及刷新混坞,網(wǎng)絡(luò)請求狐援,本地存儲等等)。這些代碼只能順序執(zhí)行究孕,無法并發(fā)執(zhí)行啥酱。
多線程
1.擁有多個線程的程序,稱作多線程程序
2.iOS 允許用戶自己開辟新的線程蚊俺,相對于主線程來講懈涛,這些線程,稱作子線程泳猬。
3.可以根據(jù)需要開辟若干子線程
4.子線程和主線程都是獨立的運行單元,各自的執(zhí)行互不影響宇植,因此能夠并發(fā)執(zhí)行得封。
3、單指郁、多線程區(qū)別
單線程程序:只有一個線程忙上,代碼順序執(zhí)行,容易出現(xiàn)代碼阻塞(頁面假死)闲坎。
多線程程序:有多個線程疫粥,線程間獨立運行茬斧,能有效的避免代碼阻塞,并且提高程序的運行性能梗逮。
注意:iOS 中關(guān)于 UI 的添加和刷新必須在主線程中操作项秉。
iOS 平臺下的多線程
iOS 多線程實現(xiàn)種類
- NSThread
- NSOperationQueue
- pthread
- GCD
NSThread
NSThread 是一個輕量級的多線程,它有以下兩種創(chuàng)建方法:
- 初始化一個子線程慷彤,但需要手動開啟
- (instancetype)initWithTarget:(id)target selector:(SEL)selector object:(id)argument
- 初始化一個子線程并自動開啟
+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument
- 開啟子線程
start
- 取消當(dāng)前子線程
cancel
- 開啟后臺執(zhí)行任務(wù)的方法:
[self performSelectorInBackground:@selector(run:) withObject:@"nsobject thread"];
注意:
1娄蔼、在多線程方法中需要添加自動釋放池
2、在應(yīng)用程序打開的時候底哗,系統(tǒng)會自動為主線程創(chuàng)建一個自動釋放池
3岁诉、我們手動創(chuàng)建的子程序需要我們手動添加自動釋放池
// NSThread 輕量級的線程,可以開啟線程和終止線程
// 創(chuàng)建一個線程,其實就是給他一個方法去執(zhí)行
// 創(chuàng)建一個子線程,專門用來打印
NSThread *thread = [[NSThread alloc] initWithTarget: self selector:@selector(actionButtonNslog) object:nil];
// 開啟這個線程
[thread start];
// 線程操作的時候
// 在主線程的時候,系統(tǒng)自動給咱們添加了一個自動釋放池,那么咱們再開啟子線程的時候,也要添加一個自動釋放池
// 如果你的線程開的比較多,會造成代碼比較亂,閱讀性不高
// 一般方法中,添加了自動釋放池,基本上都是線程方法
NSOperationQueue
NSOperation 也是抽象類 沒有實現(xiàn)具體功能
NSInvocationOperation 調(diào)用操作(相當(dāng)于任務(wù))
NSBlockOperation 在block 操作
NSOperationQueue 線程隊列
// 創(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)建一個隊列
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
// 注意 添加任務(wù)前要設(shè)置依賴關(guān)系
// 依賴性(串行)
[invocation addDependency:block2];
// 把任務(wù) 添加進隊列當(dāng)中
[queue addOperation:invocation];
[queue addOperation:block2];
// 設(shè)置線程最大并發(fā)數(shù)
queue.maxConcurrentOperationCount = 2;
// 需求 進行同步請求一張圖片 不產(chǎn)生屏幕卡頓
// 思路: 開啟一個子線程 進行同步請求圖片 圖片到手后 把圖片返回到主線程 顯示到ImageView上
}
// 實現(xiàn)任務(wù)一
- (void)invocation1
{
// 添加一個自動釋放池
@autoreleasepool {
// [NSThread currentThread] 當(dāng)前線程的信息
// [NSThread isMainThread] 是否是主線程
// 打印出來額number 是線程的個數(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
{
// 添加一個自動釋放池
@autoreleasepool {
for (int i = 0; i < 88888; i++) {
NSLog(@"%d", i);
}
}
}
pthread
不經(jīng)常使用
特點:
1 一套通用的多線程API
2 適用于Unix\Linux\Windows等系統(tǒng)
3 跨平臺\可移植
4 使用難度大
使用語言:c語言
GCD
GCD簡介
- Grand Central Dispatch 簡稱(CGD),是蘋果公司開發(fā)的技術(shù)跋选。以優(yōu)化應(yīng)用程序支持多核心處理器和其他的對稱多處理系統(tǒng)的系統(tǒng)涕癣。( 對稱多處理"(Symmetrical Multi-Processing)簡稱SMP,是指在一個計算機上匯集了一組處理器(多CPU),各CPU之間共享內(nèi)存子系統(tǒng)以及總線結(jié)構(gòu)前标。它是相對非對稱多處理技術(shù)而言的属划、應(yīng)用十分廣泛的并行技術(shù)。)
- GCD 屬于函數(shù)級的多線程候生,性能更高同眯,功能也更加強大。
- 它首次發(fā)布在 Mac OS X 10.6 , iOS4及以上也可以使用唯鸭。
GCD 核心概念
- 任務(wù):具有一定功能的代碼段须蜗。一般是一個 block 或者函數(shù)。
- 分發(fā)隊列:GCD 以隊列的方式進行工作目溉,F(xiàn)IFO明肮。
- GCD 會根據(jù)分發(fā)隊列的類型,創(chuàng)建合適數(shù)量的線程執(zhí)行隊列中的任務(wù)缭付。
- 主隊列 是GCD自帶的一種特殊串行隊列柿估,放在主隊列中的任務(wù),都會放在主線程中執(zhí)行
dispatch_get_main_queue()
- 全局隊列 隊列 任務(wù)執(zhí)行方式 并發(fā)多個任務(wù)同時執(zhí)行陷猫,串行一個一個執(zhí)行
1 串行隊列 讓任務(wù)一個接一個地執(zhí)行 (一個任務(wù)執(zhí)行完畢后秫舌,再執(zhí)行下一個任務(wù))
2 并發(fā)隊列 可以多個任務(wù)同時執(zhí)行 (自動開啟多個線程同時執(zhí)行任務(wù))
3 主隊列 專門負責(zé)調(diào)度主線程任務(wù),沒有辦法開辟新線程绣檬,任務(wù)在主線程只會順序執(zhí)行
執(zhí)行方式 執(zhí)行的順序 同步按順序執(zhí)行足陨,異步不按順序執(zhí)行
1.同步執(zhí)行 在當(dāng)前線程中執(zhí)行任務(wù),不具備開啟新線程的能力
2.異步執(zhí)行 在新的線程中執(zhí)行任務(wù)娇未,具備開啟新線程的能力
任務(wù) block
死鎖 如果向主隊列中添加一個同步任務(wù)會死鎖
死鎖原因:我們知道dispatch_sync表示同步的執(zhí)行任務(wù)墨缘,也就是說執(zhí)行dispatch_sync后,當(dāng)前隊列會阻塞。而dispatch_sync中的block如果要在當(dāng)前隊列中執(zhí)行镊讼,就得等待當(dāng)前隊列程執(zhí)行完成宽涌。
主隊列在執(zhí)行dispatch_sync,隨后隊列中新增一個任務(wù)block蝶棋。因為主隊列是同步隊列卸亮,所以block要等dispatch_sync執(zhí)行完才能執(zhí)行,但是dispatch_sync是同步派發(fā)嚼松,要等block執(zhí)行完才算是結(jié)束嫡良。在主隊列中的兩個任務(wù)互相等待,導(dǎo)致了死鎖献酗。
GCD 中兩種隊列
dispatch queue分為下面兩種:
- SerialQueue:一次只執(zhí)行一個任務(wù)寝受。Serial queue通常用于同步訪問特定的資源或數(shù)據(jù)。當(dāng)你創(chuàng)建多個Serial queue時罕偎,雖然它們各自是同步執(zhí)行的很澄,但Serial queue與Serial queue之間是并發(fā)執(zhí)行的。Serial queue能實現(xiàn)線程同步颜及。
- Concurrent:可以并發(fā)的執(zhí)行多個任務(wù)甩苛,但是遵守 FIFO。
GCD功能
- dispatch_async( ) 往隊列中添加任務(wù)俏站,任務(wù)會排隊執(zhí)行
- dispatch_after( ) 往隊列中添加任務(wù)讯蒲,任務(wù)不但會排隊,還會在延遲的時間點執(zhí)行
- dispatch_apply( ) 往隊列中添加任務(wù)肄扎,任務(wù)會重復(fù)執(zhí)行 n次
- dispatch_group_async( ) 將任務(wù)添加到隊列中墨林,并添加分組標(biāo)記
- dispatch_group_notify( ) 將任務(wù)添加到隊列中,當(dāng)某個分組的所有任務(wù)執(zhí)行完之后犯祠,此任務(wù)才會執(zhí)行
- dispatch_barrier_async( ) 將任務(wù)添加到隊列中旭等,此任務(wù)執(zhí)行的時候,其他任務(wù)停止執(zhí)行
- dispatch_once( ) 任務(wù)添加到隊列中衡载,但任務(wù)在程序運行過程中搔耕,只執(zhí)行一次
- dispatch_sync ( ) 將任務(wù)添加到隊列中,block 不執(zhí)行完痰娱,下面代碼不會執(zhí)行
- dispatch_async_f ( ) 將任務(wù)添加到隊列中弃榨,任務(wù)是函數(shù)非 block
線程間的通信
線程間通信分為兩種:
- 主線程進入子線程(前面的方法都可以)
- 子線程回到主線程
返回主線程:
GCD:dispatch_get_main_queue()
NSObject :- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait
線程互斥
線程互斥場景:
- 線程互斥是指某一資源同上=時只允許一個訪問者對其進行訪問,具有唯一性和排它性猜揪。
- 互斥無法限制訪問者對資源的訪問順序惭墓,即訪問是無序的。因此需要加上互斥鎖來進行順利訪問而姐,最具代表性的就是買票系統(tǒng)。
- NSLock 類能協(xié)助完成互斥操作划咐。
#pragma mark -- 買火車票的線程鎖實現(xiàn)
- (void)tickets
{
// 初始化票數(shù)
_totalTickets = 100;
// 初始化剩余票數(shù)
_subTickets = 100;
// 初始化線程鎖
self.lock = [[NSLock alloc] init];
// 先創(chuàng)建兩個并行隊列
dispatch_queue_t queue1 = dispatch_queue_create("火車站", DISPATCH_QUEUE_CONCURRENT);
// 給火車站,添加賣票的任務(wù)
dispatch_async(queue1, ^{
// 賣票
[self saleTickets:queue1];
});
// 創(chuàng)建12306隊列
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) {
// 添加鎖
// 線程鎖 跟自動釋放池 使用的方法差不多
// 中間夾的部分,是鎖的內(nèi)容
[self.lock lock];
// 咱們要知道,是哪個隊列來賣票
// 通過隊列表示符,得到
// dispatch_queue_get_label 得到隊列的標(biāo)識符
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方法是一個很有用的方法拴念,它的完整定義如下:
dispatch_group_wait(group: dispatch_group_t, _ timeout: dispatch_time_t) -> Int
第一個參數(shù)表示要等待的group钧萍,第二個則表示等待時間。返回值表示經(jīng)過指定的等待時間政鼠,屬于這個group的任務(wù)是否已經(jīng)全部執(zhí)行完风瘦,如果是則返回0,否則返回非0公般。
第二個dispatch_time_t類型的參數(shù)還有兩個特殊值:DISPATCH_TIME_NOW和DISPATCH_TIME_FOREVER万搔。
前者表示立刻檢查屬于這個group的任務(wù)是否已經(jīng)完成,后者則表示一直等到屬于這個group的任務(wù)全部完成官帘。
*/
}