文章分享至個人我的技術博客: cainrun.github.io/15020181423…html
前面咱們已經把經常使用的GCD
, NSOperation
, NSOperationQueue
講完了, 如今咱們繼續來看看, 另外一個不經常使用的多線程(實際上是我本身不經常使用), 它就是NSThread
.git
說到NSThread
的話, 我麼那就不得不提到它的底層叫作Pthread
, 這是一個由C
語言寫的多線程技術, 能夠在Unix
, Linux
和Windows
上使用.github
但因爲是用C
語言所寫的, 因此須要本身管理線程生命週期, 開發難度比較大(不得不佩服一下搞C
語言的大佬們), 這裏咱們簡單瞭解一下就行了, 想深刻了解的話, 能夠自行去谷歌一下.vim
轉載聲明:如須要轉載該文章, 請聯繫做者, 而且註明出處, 以及不能擅自修改本文.安全
這裏引用一段來自維基百科
的Pthread介紹:微信
POSIX
線程(英語:POSIX Threads
,常被縮寫爲Pthreads
)是POSIX
的線程標準,定義了建立和操縱線程的一套API
。多線程實現
POSIX
線程標準的庫常被稱做Pthreads
,通常用於Unix-like POSIX
系統,如Linux
、Solaris
。框架可是
Microsoft Windows
上的實現也存在,例如直接使用Windows API
實現的第三方庫pthreads-w32
;函數而利用
Windows
的SFU/SUA
子系統,則可使用微軟提供的一部分原生POSIX API
。性能
在舉例子🌰以前, 咱們得分析一下使用Pthread
的幾個步驟:
#import <pthread.h>
- (void)cratePthread {
pthread_t thread;
pthread_create(&thread, NULL, run, NULL);
}
void *run(void *param) {
NSLog(@"執行任務, 當前的線程爲: %@", [NSThread currentThread]);
return NULL;
}複製代碼
2017-08-06 20:33:42.496 NSThread-Example[4008:314745] 執行任務, 當前的線程爲: <NSThread: 0x60800007cb40>{number = 3, name = (null)}複製代碼
這裏要解釋一下pthread_create(&thread, NULL, run, NULL)
括號裏的四個屬性:
&
號加線程對象名.run
其實就是咱們以前講到的任務.NSThread
是蘋果爸爸針對Pthread
而封裝的Objective-C
對象, 有啥好處?
Pthread
管理線程生命週期代碼.以前咱們常常看到[NSThread currentThread]
這句代碼, 就是獲取當前線程信息的.
如今咱們來看看NSThread
的簡單使用:
- (void)nsthread {
NSThread *thread = [[NSThread alloc] initWithTarget:self
selector:@selector(runTheThread)
object:nil];
[thread start];
}
- (void)runTheThread {
NSLog(@"執行任務, 當前的線程爲: %@", [NSThread currentThread]);
}複製代碼
2017-08-06 20:53:32.129 NSThread-Example[4108:332154] 執行任務, 當前的線程爲: <NSThread: 0x600000265900>{number = 3, name = (null)}複製代碼
除此以外, 咱們還能夠在建立隱式線程並啓動它:
- (void)backgroundThread {
[self performSelectorOnMainThread:@selector(runTheBackgroundThread)
withObject:nil
waitUntilDone:YES];
}
- (void)runTheBackgroundThread {
NSLog(@"執行任務, 當前的線程爲: %@", [NSThread currentThread]);
}複製代碼
2017-08-06 20:57:35.971 NSThread-Example[4159:338649] 執行任務, 當前的線程爲: <NSThread: 0x608000067300>{number = 1, name = main}複製代碼
獲取主線程
+ (NSThread *)mainThread;複製代碼
上面有獲取主線程咯, 那這裏也有一個判斷是不是主線程的方法了, 區別在於, 一個是實例方法, 一個是類方法:
@property (readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0);
@property (class, readonly) BOOL isMainThread NS_AVAILABLE(10_5, 2_0); // reports whether current thread is main複製代碼
還有咱們以前常常看到的獲取線程方法:
@property (class, readonly, strong) NSThread *currentThread;
[NSThread currentThread];複製代碼
若是你想和GCD
那樣設置線程的名字或者是獲取線程的名字, 那麼你能夠用這個:
@property (nullable, copy) NSString *name NS_AVAILABLE(10_5, 2_0);複製代碼
在上面的代碼裏, 咱們能夠看到和NSOperation
同樣, 有一個- (void)start
方法, 用途是同樣的, 就是啓動線程:
- (void)start NS_AVAILABLE(10_5, 2_0);複製代碼
除了這個以外還有暫停當前線程的方法:
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;複製代碼
最後還有一個終止當前線程的方法和取消當前線程的方法:
+ (void)exit;
- (void)cancel NS_AVAILABLE(10_5, 2_0);複製代碼
既然這裏提到了操控線程的狀態, 那就得講講, 線程狀態的切換.
當咱們用NSThread
建立一條信線程的時候, 內存中會佔用一塊內存, 以下圖:
一旦咱們經過調用- (void)start
以後, 就會把當前的線程對象, 加入到線程池中, 以下圖:
若是還有其餘線程對象添加進來或者是其餘線程, 就會以下圖所示:
這裏舉幾個線程切換狀態時的幾個例子:
CPU
去調用當前線程對象的時候, 那麼當前線程就會進入一個運行狀態
, 不然就是準備就緒狀態
(好比CPU
去調用其餘線程對象), 以下圖:CPU
在運行當前線程對象的時候調用sleep方法
或者是同步鎖
, 那麼當前線程就會進入一個線程阻塞狀態
, 等到sleep方法
到時間以後, 或者獲得同步鎖
, 就會回到就緒狀態
.CPU
在運行當前線程對象的時候, 任務執行完成或者是強制退出, 那麼當前線程對象就會進入一個終止狀態
.雖然經過蘋果爸爸封裝的NSThread
或者是用C
寫的Pthread
能夠更加直接的操做線程, 但咱們通常不會直接用到NSThread
和Pthread
去操做線程.
直接操做線程的話能夠一不當心就GG
了, 什麼意思呢?
好比咱們在一個大項目裏直接操做了線程, 給你個8核CPU
, 咱們爲了徹底使用CPU
的性能, 可能會建立8
條, 或者是16
條線程, 然而在咱們代碼調用的框架裏, 又作了相似的操做(代碼不會知道咱們建立了線程), 那麼這時候就會形成上百條, 上千條線程了.
其實代碼自己是沒有問題的, 但因爲咱們的操做不當, 最終就出現了問題, 使用線程都會有消耗一些內存和內核資源, 因此爲了安全起見, 仍是不要直接操做線程了.
項目地址: github.com/CainRun/iOS…