1.進(jìn)程和線程
- 線程是進(jìn)程的基本執(zhí)行單元,一個進(jìn)程的所有任務(wù)都在線程中執(zhí)行 * 進(jìn)程要想執(zhí)行任務(wù),必須得有線程,進(jìn)程至少要有一條線程
- 程序啟動會默認(rèn)開啟一條線程,這條線程被稱為主線程或 UI 線程
- 進(jìn)程是指在系統(tǒng)中正在運行的一個應(yīng)用程序
- 每個進(jìn)程之間是獨立的,每個進(jìn)程均運行在其專用的且受保護(hù)的內(nèi)存空間內(nèi)
- 通過“活動監(jiān)視器”可以查看 Mac 系統(tǒng)中所開啟的進(jìn)程
地址空間:同一進(jìn)程的線程共享本進(jìn)程的地址空間,而進(jìn)程之間則是獨立的地址空間。 資源擁有:同一進(jìn)程內(nèi)的線程共享本進(jìn)程的資源如內(nèi)存、I/O、cpu等,但是進(jìn)程之間的 資源是獨立的。
1: 一個進(jìn)程崩潰后,在保護(hù)模式下不會對其他進(jìn)程產(chǎn)生影響,但是一個線程崩潰整個進(jìn) 程都死掉。所以多進(jìn)程要比多線程健壯。
2: 進(jìn)程切換時,消耗的資源大,效率高。所以涉及到頻繁的切換時,使用線程要好于進(jìn) 程。同樣如果要求同時進(jìn)行并且又要共享某些變量的并發(fā)操作,只能用線程不能用進(jìn)程 3: 執(zhí)行過程:每個獨立的進(jìn)程有一個程序運行的入口、順序執(zhí)行序列和程序入口。但是 線程不能獨立執(zhí)行,必須依存在應(yīng)用程序中,由應(yīng)用程序提供多個線程執(zhí)行控制。
4: 線程是處理器調(diào)度的基本單位,但是進(jìn)程不是。 5: 線程沒有地址空間,線程包含在進(jìn)程地址空間中
- 優(yōu)點
- 能適當(dāng)提高程序的執(zhí)行效率
- 能適當(dāng)提高資源的利用率(CPU,內(nèi)存)
- 線程上的任務(wù)執(zhí)行完成后,線程會自動銷毀
- 缺點
- 開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,每一個線程都占 512 KB)
- 如果開啟大量的線程,會占用大量的內(nèi)存空間,降低程序的性能
- 線程越多,CPU 在調(diào)用線程上的開銷就越大
- 程序設(shè)計更加復(fù)雜,比如線程間的通信、多線程的數(shù)據(jù)共享
/**
pthread_create 創(chuàng)建線程
參數(shù):
1. pthread_t:要創(chuàng)建線程的結(jié)構(gòu)體指針,通常開發(fā)的時候,如果遇到 C 語言的結(jié)構(gòu)體,類型后綴 `_t / Ref` 結(jié)尾
同時不需要 `*`
2. 線程的屬性,nil(空對象 - OC 使用的) / NULL(空地址,0 C 使用的)
3. 線程要執(zhí)行的`函數(shù)地址`
void *: 返回類型,表示指向任意對象的指針,和 OC 中的 id 類似
(*): 函數(shù)名
(void *): 參數(shù)類型,void *
4. 傳遞給第三個參數(shù)(函數(shù))的`參數(shù)`
返回值:C 語言框架中非常常見
int
0 創(chuàng)建線程成功!成功只有一種可能
非 0 創(chuàng)建線程失敗的錯誤碼,失敗有多種可能!
*/
// 1: pthread
pthread_t threadId = NULL;
//c字符串
char *cString = "HelloCode";
// NSString *ocString = @"Gavin";
//延伸到: OC--C的混編 尤其在智能家居,SDK封裝
//拋出一個問題: 在ARC需要這樣操作,在MRC不需要
// OC prethread -- 跨平臺
// 鎖
int result = pthread_create(&threadId, NULL, pthreadTest, cString);
if (result == 0) {
NSLog(@"成功");
} else {
NSLog(@"失敗");
}
// 2: NSThread
[NSThread detachNewThreadSelector:@selector(threadTest) toTarget:self withObject:nil];
// 3: GCD
dispatch_async(dispatch_get_global_queue(0, 0), ^{
[self threadTest];
});
// 4: NSOperation
[[[NSOperationQueue alloc] init] addOperationWithBlock:^{
[self threadTest];
}];
/**
1. 循環(huán)的執(zhí)行速度很快
2. 棧區(qū)/常量區(qū)的內(nèi)存操作也挺快
3. 堆區(qū)的內(nèi)存操作有點慢
4. I(Input輸入) / O(Output 輸出) 操作的速度是最慢的!
* 會嚴(yán)重的造成界面的卡頓,影響用戶體驗!
* 多線程:開啟一條線程,將耗時的操作放在新的線程中執(zhí)行
*/
- (void)threadTest{
NSLog(@"begin");
NSInteger count = 1000 * 100;
for (NSInteger i = 0; i < count; i++) {
// 棧區(qū)
NSInteger num = i;
// 常量區(qū)
NSString *name = @"zhang";
// 堆區(qū)
NSString *myName = [NSString stringWithFormat:@"%@ - %zd", name, num];
NSLog(@"%@", myName);
}
NSLog(@"over");
}
void *pthreadTest(void *para){
// 接 C 語言的字符串
// NSLog(@"===> %@ %s", [NSThread currentThread], para);
// __bridge 將 C 語言的類型橋接到 OC 的類型
NSString *name = (__bridge NSString *)(para);
NSLog(@"===>%@ %@", [NSThread currentThread], name);
return NULL;
}
單核CPU,在內(nèi)存中的多線程是快速切換,用戶無法感知到切換

時間片的概念:CPU在多個任務(wù)直接進(jìn)行快速的切換,這個時間間隔就是時間片
- (單核CPU)同一時間,CPU 只能處理 1 個線程 * 換言之,同一時間只有 1 個線程在執(zhí)行
- 多線程同時執(zhí)行:
- 是 CPU 快速的在多個線程之間的切換
- CPU 調(diào)度線程的時間足夠快,就造成了多線程的“同時”執(zhí)行的效果
- 如果線程數(shù)非常多
- CPU 會在 N 個線程之間切換,消耗大量的 CPU 資源 * 每個線程被調(diào)度的次數(shù)會降低,線程的執(zhí)行效率降低
多核CPU,才是多個線程同時執(zhí)行
2.線程屬性,命名name

3.線程生命周期

線程池

飽和策略


線程中的所有任務(wù)都能cancel掉嗎?正在執(zhí)行的任務(wù)無法cancel,正在執(zhí)行的任務(wù)已經(jīng)被調(diào)度上去了

4.threadPriority優(yōu)先級qualityOfService,優(yōu)先級越高表明執(zhí)行速度越快,還與任務(wù)的復(fù)雜度有關(guān)
5.多任務(wù)在一條線程,會有資源搶奪的問題
-
1.買票問題加一把鎖:@synchronized(self),同樣可以加NSLock,信號量,柵欄函數(shù),同步
image.png
互斥鎖小結(jié)
保證鎖內(nèi)的代碼,同一時間,只有一條線程能夠執(zhí)行!
互斥鎖的鎖定范圍,應(yīng)該盡量小,鎖定范圍越大,效率越差!
互斥鎖參數(shù),能夠加鎖的任意 NSObject 對象
注意:鎖對象一定要保證所有的線程都能夠訪問
如果代碼中只有一個地方需要加鎖,大多都使用 self,這樣可以避免單獨再建一個鎖對象 -
2.atomic和nonatomic
nonatomic 非原子屬性
atomic 原子屬性(線程安全),針對多線程設(shè)計的,默認(rèn)值
保證同一時間只有一個線程能夠?qū)懭?但是同一個時間多個線程都可以取值) atomic 本身就有一把鎖(自旋鎖) 單寫多讀:單個線程寫入,多個線程可以讀取
atomic:線程安全,需要消耗大量的資源 nonatomic:非線程安全,適合內(nèi)存小的移動設(shè)備
iOS 開發(fā)的建議
所有屬性都聲明為 nonatomic
盡量避免多線程搶奪同一塊資源 盡量將加鎖、資源搶奪的業(yè)務(wù)邏輯交給服務(wù)器端處理,減小移動客戶端的壓力
image.png - 3.自旋鎖和互斥鎖
互斥鎖:發(fā)現(xiàn)其他線程執(zhí)行,當(dāng)前線程 休眠(就緒狀態(tài)) 一直在等打開 喚醒執(zhí)行
自旋鎖:發(fā)現(xiàn)其他線程執(zhí)行,當(dāng)前線程 詢問 - 忙等 耗費性能比較高
短小用自旋鎖
任務(wù)復(fù)雜度比較高用互斥鎖
6.線程間通訊
- (void)viewDidLoad {
[super viewDidLoad];
_imageView = [[UIImageView alloc] init];
// 1. 準(zhǔn)備 URL
NSString *urlString = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1529488045873&di=7c1cc40bec638406ef48717386a08094&imgtype=0&src=http%3A%2F%2Fpic2.ooopic.com%2F12%2F46%2F41%2F95bOOOPIC74_1024.jpg";
NSURL *url = [NSURL URLWithString:urlString];
// 2. 下載圖像
//[self downloadImageWithURL:url];
// 異步下載圖像
[self performSelectorInBackground:@selector(downloadImageWithURL:) withObject:url];
}
/**
* 下載 URL 指定的網(wǎng)絡(luò)圖片
*/
- (void)downloadImageWithURL:(NSURL *)url {
NSLog(@"%@", [NSThread currentThread]);
// 1. 所有從網(wǎng)絡(luò)返回的都是二進(jìn)制數(shù)據(jù)
NSData *data = [NSData dataWithContentsOfURL:url];
// 2. 將二進(jìn)制數(shù)據(jù)轉(zhuǎn)換成 image
UIImage *image = [UIImage imageWithData:data];
// 3. 在主線程更新 UI
// waitUntilDone: 是否等待 updateImage: 執(zhí)行完成
[self performSelectorOnMainThread:@selector(updateImage:) withObject:image waitUntilDone:YES];
NSLog(@"完成");
}
/**
* 更新圖像 UI
*/
- (void)updateImage:(UIImage *)image {
NSLog(@"更新 UI -> %@", [NSThread currentThread]);
// 3. 設(shè)置圖像
_imageView.image = image;
// 4. 調(diào)整大小
[_imageView sizeToFit];
// 5. 設(shè)置 contentSize
_scrollView.contentSize = image.size;
}
7.端口間通信
把port加入runloop,[[NSRunLoop currentRunLoop] addPort:self.myPort forMode:NSDefaultRunLoopMode],接收port消息的端口加入runloop里面


8.UIImageView用weak修飾
weak修飾ImageView,一創(chuàng)建就釋放了,棧區(qū)的空間系統(tǒng)自動管理回收,無法進(jìn)行添加,可以通過添加中間變量進(jìn)行賦值,引用計數(shù)進(jìn)行傳遞,在函數(shù)viewDidLoad中都不會被銷毀



