iOS開發之多執行緒NSThread
之前的文章中介紹,多執行緒能夠提高應用程式的執行效率,把耗時的操作放在子執行緒中執行,這樣不會阻塞主執行緒的執行,不會給使用者“卡”的體驗。本文主要介紹多執行緒程式設計的第一種方式。
由於pthread是底層的多執行緒程式設計API,對於pthread,瞭解如何讓建立子執行緒即可。使用pthread,需要匯入標頭檔案#import<pthread.h>
建立子執行緒如下:
void *run(void *data) { NSThread *current = [NSThread currentThread]; for (int i = 0; i<20000; i++) { NSLog(@"run---%@", current); } return NULL; } - (IBAction)btnClick { // 1.獲得當前的執行緒 NSThread *current = [NSThread currentThread]; NSLog(@"btnClick---%@", current); // 2.執行一些耗時操作 : 建立一條子執行緒 pthread_t threadId; pthread_create(&threadId, NULL, run, NULL); }
NSThread
優點:NSThread 比GCD和NSOperation兩個輕量級
缺點:需要自己管理執行緒的生命週期,執行緒同步。執行緒同步對資料的加鎖會有一定的系統開銷
建立和啟動執行緒
一個NSThread物件就代表一條執行緒。
首先來了解關於NSThread的一些方法:
主執行緒相關用法
+ (NSThread *)mainThread; // 獲得主執行緒
- (BOOL)isMainThread;// 是否為主執行緒
+ (BOOL)isMainThread; // 是否為主執行緒
獲得當前執行緒
NSThread *current = [NSThreadcurrentThread];
執行緒的排程優先順序
+ (double)threadPriority;
+ (BOOL)setThreadPriority:(double)p;
- (double)threadPriority;
- (BOOL)setThreadPriority:(double)p;
排程優先順序的取值範圍是0.0 ~ 1.0,預設0.5,值越大,優先順序越高
執行緒的名字
- (void)setName:(NSString *)n;
- (NSString *)name;
1. 建立、啟動執行緒
NSThread *thread= [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil]; [thread start];// 執行緒一啟動,就會線上程thread中執行self的run方法
2. 建立執行緒後自動啟動執行緒
[NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
3. 隱式建立並自動啟動執行緒
[self performSelectorInBackground:@selector(run) withObject:nil];
建立執行緒方式2、3優點:簡單快捷;缺點:無法對執行緒進行更詳細的設定。
執行緒狀態
知道如何建立執行緒,那麼我們來看看,執行緒從建立到死亡(銷燬)都經歷了哪些狀態。
我們利用如下程式碼來講解:
NSThread *thread = [[NSThread alloc]initWithTarget:self selector:@selector(run) object:nil];
[thread start];
1>當執行緒被建立開始,執行緒就進入了新建狀態
2>當呼叫start方法後,執行緒有可能沒有被CPU排程則執行緒進入了就緒狀態,如果呼叫start方法後,執行緒直接被CPU排程則將執行緒就進入了執行狀態
3>當CPU排程了其他執行緒,則當前執行緒就進入就緒狀態
4>當呼叫了sleep方法\等待同步鎖的時候,執行緒暫停執行,則執行緒進入了阻塞狀態
5>當阻塞狀態下得執行緒sleep到時\得到同步鎖後,執行緒又進入了就緒狀態
6>當執行緒任務完成\異常退出\強制退出,則執行緒就進入死亡狀態,執行緒就被銷燬。
如下圖:
注意:執行緒建立後,會被放在程序中可排程執行緒池中,可排程執行緒池中的執行緒在CPU的排程下執行。當執行緒被阻塞\死亡,則執行緒會從可排程執行緒池中移出\銷燬。
控制器執行緒狀態的方法
啟動執行緒
- (void)start;// 進入就緒狀態 -> 執行狀態。當執行緒任務執行完畢,自動進入死亡狀態
阻塞(暫停)執行緒
+ (void)sleepUntilDate:(NSDate *)date;
+(void)sleepForTimeInterval:(NSTimeInterval)ti;// 進入阻塞狀態
強制停止執行緒
+ (void)exit;// 進入死亡狀態
注意:一旦執行緒停止(死亡)了,就會被銷燬,就不能再次開啟任務
多執行緒雖然能提高應用程式的執行效率,同時多執行緒也存在一定的安全隱患。
1. 資源共享
一塊資源可能會被多個執行緒共享,也就是多個執行緒可能會訪問同一塊資源。比如多個執行緒訪問同一個物件、同一個變數、同一個檔案
2. 當多個執行緒訪問同一塊資源時,很容易引發資料錯亂和資料安全問題
如下圖:講解:有一個整形變數number = 17,執行緒A要訪問變數number,並獲得它的值為17,執行+1操作;這個時候執行緒B也訪問了變數number,並獲得它的值(此時執行緒A還沒執行寫入操作),則值為17,執行了+1操作,最後才執行寫入操作。執行緒A寫入完得18,執行緒B寫入完也是18。這樣就引發了資料安全的問題。
在iOS開發中,多執行緒是非常重要的技術,那麼如何避免多執行緒帶來的安全隱患呢?
在多執行緒訪問同一資源的情況下,可以將資源進行加鎖(互斥鎖\同步鎖)。
如下圖:
講解:有一個整形變數number = 17,執行緒A讀取變數number之後,變數number被加上鎖,再執行操作;這個時候執行緒B也要訪問變數number,因為被加上了鎖,所以執行緒B無法訪問。等執行緒A對變數number執行完操作後,number會開啟鎖,這樣執行緒B就可以訪問,訪問的時候也要加鎖,執行完操作後,再把鎖開啟,這樣就可以避免資料不安全的隱患了。
在iOS開發中實現加鎖如下:
互斥鎖使用格式(任何物件都可以當做一把鎖)
@synchronized(鎖物件) { //需要鎖定的程式碼}
注意:鎖定1份程式碼只用1把鎖,用多把鎖是無效的
互斥鎖的優缺點
優點:能有效防止因多執行緒搶奪資源造成的資料安全問題
缺點:需要消耗大量的CPU資源
互斥鎖的使用前提:多條執行緒搶奪同一塊資源
相關專業術語:執行緒同步
執行緒同步的意思是:多條執行緒按順序地執行任務
互斥鎖,就是使用了執行緒同步技術
其實在開發中我們經常遇到加鎖的加鎖的情況,只是我們沒有使用。就是原子和非原子屬性
OC在定義屬性時有nonatomic和atomic兩種選擇
atomic:原子屬性,為setter方法加鎖(預設就是atomic)
nonatomic:非原子屬性,不會為setter方法加鎖
atomic加鎖原理
@property (assign, atomic) int age;
- (void)setAge:(int)age
{
@synchronized(self){
_age = age;
}
}
nonatomic和atomic對比
atomic:執行緒安全,需要消耗大量的資源
nonatomic:非執行緒安全,適合記憶體小的移動裝置
執行緒間通訊
既然多執行緒提高了應用程式的執行效率,但是在多條執行緒之間要想進行資料互動怎麼辦呢?這就用到了執行緒間通訊。
執行緒間通訊:在1個程序中,執行緒往往不是孤立存在的,多個執行緒之間需要經常進行通訊
執行緒間通訊的體現
1.1個執行緒傳遞資料給另1個執行緒
2.在1個執行緒中執行完特定任務後,轉到另1個執行緒繼續執行任務
執行緒間通訊常用方法
-(void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)argwaitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelectoronThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;
執行緒間通訊如圖:
IOS開發中實現多執行緒技術的第一種方法就介紹到這裡。