天天看點

iOS學習——多線程開發(NSThread)

  具體來說,iOS方面多線程也就是兩種,pthread以及NSThread。pthread是C語言寫的多線程,好處是不僅僅用在iOS移動端開發,基本上支援C語言的都可以使用,缺點就是C語言的共性了,不易識别,難記而且并不支援arc。是以在iOS中多線程開發還是去了解NSThread更好,pthread掌握即可。

  在oc對象的使用中,最為常見的就是alloc與init,對象記憶體配置設定和執行個體化。是以就有可能出現這種情況:

    NSThread *thread1 = [[NSThread alloc] init];

    [thread1 start];

  實際上這是不行的,NSThread無法直接alloc init,但是可以派生子類來執行個體化。要使用線程對象,可以通過這種方式:

 NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(runThread) object:nil];

    [thread2 start];

當然如果不想使用對象方式開啟線程,有更簡便的方式:

    [NSThread detachNewThreadSelector:@selector(runThread) toTarget:self withObject:nil];

當然,這種方式就沒辦法使用線程的一些屬性了。可以用thread2.name 來給線程自定義一個名字,這樣當出現線程崩潰的情況,可以在左邊的崩潰清單直接定位到是哪個線程出現了問題。另外可以通過threadPriority來設定線程的優先級,不過設定優先級僅僅增加了線程的被調用機率,而不是完全先調用優先級高的線程。

iOS學習——多線程開發(NSThread)

可以看到,雖然優先調用了thread2,但是其中仍然穿插了thread3的調用。(在多線程開發中,不要相信一次或者少數次運作結果);

  線程的狀态包括五種,nsthread可以幫我們管理其中的四種,一種是線上程池中建立一個線程,第二種是[thread start],這并不是直接開啟線程,而是讓線程進入準備狀态,runloop可以随時調用,第三種就是runing 了,這個是我們無法控制的,第四個是sleep狀态或者叫阻塞狀态,當線程運作完或我們調用sleep就會進行這種狀态,第五種就是dead 了,可以通過exit來殺死線程。

  進一步學習,我們使用線程是用來幹什麼的呢?當然是耗時操作不友善放在主線程種調用。那麼我們處理完資料後,還是要去修改UI,難道是直接修改就可以了?當然不是,iOSUI使用都是在主線程中,所有就有了線程間通訊技術使用,保證安全準确更新UI

- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

第一個SEL是在主線程種執行的方法,第二個arg是需要傳入的參數,第三個wait是表示目前線程是否等待,如果為YES,那麼隻有等待sel方法執行完了,線程才會繼續進行。

在子線程中,或許你可以更新UI成功,但是這會出現很多不确定性問題,譬如你今天運作OK,明天再次運作就會挂了,讓你完全摸不着頭腦。

提到上面這個方法,就必定了解到另外一個

    [self performSelector:@selector(othreRunThread) onThread:thread2 withObject:nil waitUntilDone:NO];

iOS學習——多線程開發(NSThread)

你會發現,當你送出函數進入thread2中,函數并沒有被執行。這就涉及到runloop,runloop會開啟一個死循環,讓線程的執行形成一個圓,當執行完畢就會從頭開始再次執行,不斷循環詢問系統是否有方法需要執行,我們開啟的子線程并沒有加入循環中,就像一條線,它已經走到了底部,你再線上的起點加入函數,線程也不會回頭去執行。我們可以線上程中使用實作runloop

- (void)runThread{

    [[NSRunLoop currentRunLoop] run];

        NSLog(@"------%@,%@-------",NSStringFromSelector(_cmd),[NSThread currentThread]);

}

不過這并不是一個很好的選擇,這樣會開啟一個始終占用資源的死循環線程,我們并沒有很好的辦法去停止這個循環。

- (void)runThread{

    while (!self.isfinshed) {

        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1f]];

    }

        NSLog(@"------%@,%@-------",NSStringFromSelector(_cmd),[NSThread currentThread]);

}

- (void)othreRunThread{

    self.finshed = YES;

        NSLog(@"------%@,%@-------",NSStringFromSelector(_cmd),[NSThread currentThread]);

}

我們可以用一個全局的判斷條件來進行設定,當othreRunThread未被執行的時候,我們就開啟循環0.1秒,不斷的讓線程去詢問是否有方法未被執行,當方法已經執行後,就不再進行循環。

  最後我們得知道,cpu僅能同時處理一條線程,多線程并發并不是多條線程同時進行,而是cpu不斷線上程間切換進行,是以線程并不是越多越好,當存在大量線程,會讓cpu在切換間疲于奔命,反而不利于開發。而setStackSize則可以主動配置設定一個線程所占記憶體大小,讓線程運作更加合理。

轉載于:https://www.cnblogs.com/zhulilove/p/7805725.html