iOS開發中,多線程相關的知識點主要包括pthread、NSThread、NSOperation和GCD,我們經常用到的就數NSOperation和GCD了。學習了一段時間后,覺得有必要總結鞏固一下,對自己也是一種提高。pthread和NSThread內容不多,所以放在同一篇,NSOperation和GCD各一篇,總共三篇。
本篇文章主要內容:
- 簡單介紹pthread
- NSThread的使用
pthread
POSIX線程(POSIX threads),簡稱Pthreads,是線程的POSIX標準。該標準定義了創建和操縱線程的一整套API。在類Unix操作系統(Unix、Linux、Mac OS X等)中,都使用Pthreads作為操作系統的線程。
簡單來說就是操作系統級別使用的線程,基于c語言實現,我們的OC代碼中很少用到,并且不便于管理。在pthread.h中,我們可以看到很多操作線程的方法:
pthread_create( ) : 創建一個線程
pthread_exit ( ) : 退出當前線程
pthread_main_np ( ) : 獲取主線程
......
這些方法具體怎么用,本篇文章不再關注,有興趣的童鞋可自行研究,這里看下互斥鎖的相關內容。互斥鎖的作用是防止多個線程同時訪問臨界區引起的臟數據或者數據損壞問題。pthread_mutex的用法如下:
pthread_mutex_t _lock;
pthread_mutex_init(&_lock, NULL); //初始化一個互斥鎖
pthread_mutex_lock(&_lock); //加鎖,線程進入臨界區,其他線程在外面等待
...... //執行臨界區代碼
pthread_mutex_unlock(&_lock); //解鎖,線程離開臨界區,其他線程進入臨界區執行
pthread_mutex_destroy(&_lock); //最后銷毀互斥鎖
互斥鎖保證了臨界區代碼在某個時刻只有一個線程在執行。iOS開發中還有其他類型的鎖,以后會再寫一篇文章單獨介紹。
NSThread的使用
創建
類方法
detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
detachNewThreadWithBlock:(void (^)(void))block; //iOS10新增方法,以塊的形式執行
實例方法
initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
initWithBlock:(void (^)(void))block; //iOS10新增方法,以塊的形式執行
執行
對于類方法創建的線程會自動執行,而實例方法創建的線程需要調用start才能執行
配置線程
設置線程名稱:[[NSThread currentThread] setName:@"xxxx"]
設置線程優先級:(BOOL)setThreadPriority:(double)p
......
操作線程
cancel //取消線程
exit //停止線程
sleepUntilDate:(NSDate *)date //線程休眠到某個時間點
sleepForTimeInterval:(NSTimeInterval)ti //線程休眠一段時間
獲取線程信息
(double)threadPriority;//獲取線程優先級
BOOL isMainThread; //當前線程是否是主線程,開發中比較有用
BOOL executing;//是否正在執行
BOOL finished; //是否已經結束
BOOL cancelled;//是否被取消了
線程間通信
//在主線程執行方法
performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;
//在某個線程執行方法
performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait
//在后臺線程執行方法
performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg
部分實例代碼如下,完整鏈接點這里:
@implementation ViewController
#pragma mark - LifeCycle
- (void)viewDidLoad {
[super viewDidLoad];
[self initUI];
//以block形式執行,類方法
[NSThread detachNewThreadWithBlock:^{
[[NSThread currentThread] setName:@"block線程"];
[NSThread sleepForTimeInterval:0.5];
NSString *info = [NSString stringWithFormat:@"detach新線程執行Block,thread info:%@", [NSThread currentThread]];
[self performSelectorOnMainThread:@selector(fillLabel:) withObject:info waitUntilDone:NO];
}];
//以方法形式執行,類方法
[NSThread detachNewThreadSelector:@selector(detachThreadExcuteMethod) toTarget:self withObject:nil];
}
#pragma mark - Getter
- (UIButton *)startButton
{
if (!_startButton) {
UIButton *startButton = [[UIButton alloc] initWithFrame:CGRectMake(100, 280, 150, 30)];
[startButton setTitle:@"點擊開始下載圖片" forState:UIControlStateNormal];
[startButton setTitleColor:[UIColor blueColor] forState:UIControlStateNormal];
[startButton addTarget:self action:@selector(startDownload) forControlEvents:UIControlEventTouchUpInside];
_startButton = startButton;
}
return _startButton;
}
- (NSThread *)downloadThread
{
if (!_downloadThread) {
//以方法形式執行,實例方法,需要手動開始
NSThread *downloadThread = [[NSThread alloc] initWithTarget:self selector:@selector(downloadPicture) object:nil];
[downloadThread setName:@"download thread"];
_downloadThread = downloadThread;
}
return _downloadThread;
}
#pragma mark - Private
- (void)initUI
{
[self.view addSubview:self.threadInfoTextView];
[self.view addSubview:self.startButton];
[self.view addSubview:self.imageView];
}
#pragma mark - Action
- (void)detachThreadExcuteMethod
{
[[NSThread currentThread] setName:@"method線程"];
[NSThread sleepForTimeInterval:0.5];
NSString *info = [NSString stringWithFormat:@"detach新線程執行方法,thread info:%@",[NSThread currentThread]];
NSMutableString *str = [NSMutableString stringWithString:info];
for (NSInteger i = 0; i < 5; i++) {
[str appendString:[NSString stringWithFormat:@"\n第%@次循環", [NSNumber numberWithInteger:i].stringValue]];
}
[self performSelectorOnMainThread:@selector(fillLabel:) withObject:str waitUntilDone:NO];
}
- (void)downloadPicture
{
NSError *error;
NSData *imageData = [[NSData alloc] initWithContentsOfURL:
[NSURL URLWithString:@"https://www.baidu.com/img/bd_logo1.png"]
options:0 error:&error];
if(imageData == nil) {
NSLog(@"Error: %@", error);
} else {
UIImage *image = [[UIImage alloc] initWithData:imageData];
[self performSelectorOnMainThread:@selector(fillPicture:) withObject:image waitUntilDone:NO];
}
}
- (void)startDownload
{
//線程執行完成后會死掉,如果再次調用其start方法會crash
//線程正在執行中,如果再次調用其start方法也會crash
if ([self.downloadThread isFinished] || [self.downloadThread isExecuting]) {
return;
}
[self.downloadThread start];
}
- (void)fillPicture:(UIImage *)image
{
self.imageView.image = image;
}
- (void)fillLabel:(NSString *)info
{
NSMutableString *str = [NSMutableString stringWithString:self.threadInfoTextView.text];
[str appendString:@"\n\n"];
[str appendString:info];
self.threadInfoTextView.text = str;
}
@end
另外一點,pthread和NSThread是一一對應的關系,例如兩者都提供了獲取主線程和當前線程的方法。
至此,pthread和NSThread的介紹就差不多完了,兩者在開發中用途不是很多,所以也沒有做特別深入的研究,例如線程順序執行、線程同步等問題。這是本人寫的第一篇博客,肯定有不正確或者不恰當的地方,希望通過以后更多的寫作實踐得以改善。