*線程與進(jìn)程
- 進(jìn)程
進(jìn)程是指在系統(tǒng)中正在運(yùn)行的一個(gè)應(yīng)用程序
- 線程
線程是進(jìn)程的基本單元,一個(gè)進(jìn)程的多有任務(wù)都是在線程中執(zhí)行的(一個(gè)進(jìn)程至少有一個(gè)線程組成)
*多線程
多線程是指從軟件或者是硬件上實(shí)現(xiàn)多個(gè)線程并發(fā)執(zhí)行的技術(shù)。多線程技術(shù)使得計(jì)算機(jī)能夠在同一時(shí)間執(zhí)行多個(gè)線程,從而提高其整體的處理性能(解決程序的堵塞,提高程序的執(zhí)行效率)。打開Nac系統(tǒng)的活動監(jiān)測器,可以看到當(dāng)前系統(tǒng)的執(zhí)行的進(jìn)程,如圖

- 進(jìn)程作為系統(tǒng)進(jìn)行分配和調(diào)度的基本單位。主要包含三個(gè)特征:
一、獨(dú)立性
進(jìn)程是一個(gè)能夠獨(dú)立運(yùn)行的基本單位,它既擁有自己獨(dú)立的資源,又擁有著自己獨(dú)立的私有空間。在沒有經(jīng)過進(jìn)程本身的允許情況下,一個(gè)用戶的進(jìn)程是不可以直接訪問其他進(jìn)程的地址空間。
二、動態(tài)性
進(jìn)程的實(shí)質(zhì)就是程序在系統(tǒng)中的一次執(zhí)行過程。進(jìn)程有自己的生命周期和各自不同的狀態(tài),進(jìn)程是靜態(tài)消亡的。
三、并發(fā)性
多個(gè)進(jìn)程可以在單個(gè)處理器上同事執(zhí)行,而互不影響。
多線程優(yōu)/缺點(diǎn)
*優(yōu)點(diǎn):
能適當(dāng)?shù)奶岣叱绦虻膱?zhí)行效率、資源的利用率(CPU,內(nèi)存),線程上的任務(wù)執(zhí)行完成后,線程會自動銷毀。
*缺點(diǎn)
開啟線程需要占用一定的內(nèi)存空間(默認(rèn)情況下,每個(gè)線程都占512kB).
如果開啟大量的線程,會占用大量的內(nèi)存空間,降低程序的性能.
線程越多,CPU在調(diào)用線程的開銷就越大.
程序設(shè)計(jì)更加復(fù)雜,比如線程間的通信、多線程的數(shù)據(jù)共享.
線程的串行和并行
簡單的講一下,如果在一個(gè)進(jìn)程中,只有一個(gè)線程,執(zhí)行任務(wù)時(shí)只能一個(gè)一個(gè)的執(zhí)行,稱之為“串行”;如果在一個(gè)進(jìn)程中,有多個(gè)線程,每條線程之間可以同時(shí)執(zhí)行不同的任務(wù),稱之為“并行”;
主線程
一個(gè)程序運(yùn)行后,系統(tǒng)會默認(rèn)的開啟一個(gè)線程,稱之為“主線程”/“UI線程”。
主線程一般用來刷新UI界面,處理UI事件(點(diǎn)擊、滾動、拖動等事件)。
****為了保持操作的流暢,一般不會將耗時(shí)的操作放在主線程中執(zhí)行。

GCD pthread (函數(shù))直接調(diào)用
NSThread NSOperation(方法) 面向?qū)ο?br> *最常用的是基于GCD的NSOperation方案
*使用NSThread實(shí)現(xiàn)多線程
創(chuàng)建一個(gè)NSThread類的實(shí)例作為一個(gè)線程,一個(gè)線程就是一個(gè)NSThread對象。
<pre>//方式一
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
NSThread *thread 1= [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:@"小明"];
[thread start];
//方式二
[NSThread detachNewThreadSelector:@selector(demo) toTarget:self withObject:nil];
//方式三
[self performSelectorInBackground:@selector(demo) withObject:nil];
- (void) demo{
NSLog(@"hello thread ");
}
- (void) demo :(NSString *)name{
NSLog(@"hello thread %@ ",name);
} </pre>
線程狀態(tài)
話不多說,直接用向大家來展示一下

- 線程阻塞時(shí)的兩個(gè)方法
+ (void)sleepUntilDate:(NSDate *)date; + (void)sleepForTimeInterval:(NSTimeInterval)ti; 比較常用
測試線程阻塞的代碼
// 創(chuàng)建一個(gè)線程(當(dāng)線程結(jié)束,不能再次使用) NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil]; [thread start]; - (void)demo{ for (int i = 0; i <= 20; i++) { NSLog(@"當(dāng)前的i的值%d",i); //當(dāng)線程阻塞時(shí) if (i == 5) { [NSThread sleepForTimeInterval:3.0]; } //線程死亡
if (i == 10) {
[NSThread exit];
}
}
}
線程的屬性
- 線程的名稱
設(shè)置線程名稱可以當(dāng)線程執(zhí)行的方法內(nèi)部出現(xiàn)異常的時(shí)候記錄異常和當(dāng)前線程
- 線程的優(yōu)先級
內(nèi)核調(diào)度算法再決定該運(yùn)行那個(gè)線程時(shí),會把線程的優(yōu)先級作為考量因素,較高的優(yōu)先級的線程會比低的優(yōu)先級更具有執(zhí)行的時(shí)間,只是相比較低的優(yōu)先級的線程,他更有可能被調(diào)度器選擇執(zhí)行而已。切記,不是優(yōu)先級高,就一定會被先執(zhí)行。
用于測試優(yōu)先級的代碼
//多線程操作
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
// 創(chuàng)建第一個(gè)線程
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
//程序的優(yōu)先級
thread1.threadPriority = 1;
//就緒狀態(tài)
[thread1 start];
//名稱
thread1.name = @"thread1";
// 創(chuàng)建第二個(gè)線程
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(demo) object:nil];
//程序的優(yōu)先級
thread2.threadPriority = 0;
//就緒狀態(tài)
[thread2 start];
//名稱
thread2.name = @"thread2";
}
- (void)demo{
for (int i = 0; i <= 20; i++) {
NSLog(@"當(dāng)前的i的值%d,%@",i,[NSThread currentThread]);
}

線程間的安全隱患

- 兩個(gè)程序同時(shí)讀取當(dāng)前的票數(shù)為1000,然后窗口一賣出一張票,是票數(shù)剩余999,同時(shí)窗口二也售出一張票,使票數(shù)變成999。結(jié)果售出兩張票,票數(shù)卻為999,這就造成了數(shù)據(jù)的錯(cuò)誤。我們可以通過枷鎖的方式來解決這個(gè)問題。


*通過加鎖可以保證某一個(gè)時(shí)刻只能有一個(gè)線程訪問資源,防止了其他的線程搶奪資源。
下面通過一個(gè)賣票案列(加鎖),來讓大家更好的理解線程安全問題
//剩余票數(shù)
@property (nonatomic, assign) int leftTicketCount;
@end
@implementation ViewController
//買票
- (void)saleTickets:(NSString *)welcome{
while (true) {
// 模擬延遲
[NSThread sleepForTimeInterval:1.0];
//添加鎖
@synchronized (self) {
//判斷是否有票
if (self.leftTicketCount > 0) {
self.leftTicketCount--;
NSLog(@"售票處--%@,%@賣了一張票,剩余%d張數(shù)",welcome,[NSThread currentThread].name,self.leftTicketCount);
}else{
NSLog(@"沒有票了");
return;
}
}
}
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
self.leftTicketCount = 20;
NSThread *thread1 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets:) object:@"歡迎"];
NSThread *thread2 = [[NSThread alloc]initWithTarget:self selector:@selector(saleTickets:) object:@"哈哈"];
thread1.name = @"1號窗口";
thread2.name = @"2號窗口";
[thread1 start];
[thread2 start];
}


- 比較兩次的結(jié)果,明顯可以看出,加鎖后數(shù)據(jù)的穩(wěn)定。
加鎖的語法
@synchronized (obj) {
//插入被修飾的代碼塊
}
- obj只是個(gè)對象,添加鎖線程對象后,鎖對象就實(shí)現(xiàn)了對多線程的監(jiān)控,保證同一時(shí)刻只有一個(gè)線程執(zhí)行,當(dāng)同步代碼塊執(zhí)行完以后,鎖對象就會釋放對同步監(jiān)視器的鎖定(同步鎖只要有一個(gè)就可以了,同步鎖監(jiān)視整個(gè)線程的的整個(gè)運(yùn)行狀態(tài),考慮到同步鎖的生命周期,通常推薦使用當(dāng)前的線程所在的控制器作為同步鎖)