iOS多線程之pthread和NSThread

iOS開發(fā)中,多線程相關的知識點主要包括pthreadNSThreadNSOperationGCD,我們經常用到的就數(shù)NSOperationGCD了。學習了一段時間后,覺得有必要總結鞏固一下,對自己也是一種提高。pthreadNSThread內容不多,所以放在同一篇,NSOperationGCD各一篇,總共三篇。

本篇文章主要內容:

  • 簡單介紹pthread
  • NSThread的使用

pthread

POSIX線程(POSIX threads),簡稱Pthreads,是線程的POSIX標準。該標準定義了創(chuàng)建和操縱線程的一整套API。在類Unix操作系統(tǒng)(Unix、Linux、Mac OS X等)中,都使用Pthreads作為操作系統(tǒng)的線程。

簡單來說就是操作系統(tǒng)級別使用的線程,基于c語言實現(xiàn),我們的OC代碼中很少用到,并且不便于管理。在pthread.h中,我們可以看到很多操作線程的方法:

pthread_create( ) : 創(chuàng)建一個線程
pthread_exit ( ) : 退出當前線程
pthread_main_np ( ) : 獲取主線程
......

這些方法具體怎么用,本篇文章不再關注,有興趣的童鞋可自行研究,這里看下互斥鎖的相關內容。互斥鎖的作用是防止多個線程同時訪問臨界區(qū)引起的臟數(shù)據(jù)或者數(shù)據(jù)損壞問題。pthread_mutex的用法如下:

pthread_mutex_t _lock;
pthread_mutex_init(&_lock, NULL); //初始化一個互斥鎖
pthread_mutex_lock(&_lock); //加鎖,線程進入臨界區(qū),其他線程在外面等待
...... //執(zhí)行臨界區(qū)代碼
pthread_mutex_unlock(&_lock); //解鎖,線程離開臨界區(qū),其他線程進入臨界區(qū)執(zhí)行
pthread_mutex_destroy(&_lock); //最后銷毀互斥鎖

互斥鎖保證了臨界區(qū)代碼在某個時刻只有一個線程在執(zhí)行。iOS開發(fā)中還有其他類型的鎖,以后會再寫一篇文章單獨介紹。

NSThread的使用

創(chuàng)建

類方法
detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(nullable id)argument;
detachNewThreadWithBlock:(void (^)(void))block; //iOS10新增方法,以塊的形式執(zhí)行

實例方法
initWithTarget:(id)target selector:(SEL)selector object:(nullable id)argument;
initWithBlock:(void (^)(void))block; //iOS10新增方法,以塊的形式執(zhí)行

執(zhí)行

對于類方法創(chuàng)建的線程會自動執(zhí)行,而實例方法創(chuàng)建的線程需要調用start才能執(zhí)行

配置線程

設置線程名稱:[[NSThread currentThread] setName:@"xxxx"]
設置線程優(yōu)先級:(BOOL)setThreadPriority:(double)p
......

操作線程

cancel //取消線程
exit //停止線程
sleepUntilDate:(NSDate *)date //線程休眠到某個時間點

sleepForTimeInterval:(NSTimeInterval)ti //線程休眠一段時間

獲取線程信息

(double)threadPriority;//獲取線程優(yōu)先級
BOOL isMainThread; //當前線程是否是主線程,開發(fā)中比較有用
BOOL executing;//是否正在執(zhí)行
BOOL finished; //是否已經結束
BOOL cancelled;//是否被取消了

線程間通信

//在主線程執(zhí)行方法
performSelectorOnMainThread:(SEL)aSelector withObject:(nullable id)arg waitUntilDone:(BOOL)wait;

//在某個線程執(zhí)行方法
performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(nullable id)arg waitUntilDone:(BOOL)wait

//在后臺線程執(zhí)行方法
performSelectorInBackground:(SEL)aSelector withObject:(nullable id)arg

部分實例代碼如下,完整鏈接點這里

@implementation ViewController

#pragma mark - LifeCycle

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [self initUI];
    
    //以block形式執(zhí)行,類方法
    [NSThread detachNewThreadWithBlock:^{
        [[NSThread currentThread] setName:@"block線程"];
        [NSThread sleepForTimeInterval:0.5];
        NSString *info = [NSString stringWithFormat:@"detach新線程執(zhí)行Block,thread info:%@", [NSThread currentThread]];
        [self performSelectorOnMainThread:@selector(fillLabel:) withObject:info waitUntilDone:NO];
    }];
    
    //以方法形式執(zhí)行,類方法
    [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) {
        //以方法形式執(zhí)行,實例方法,需要手動開始
        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新線程執(zhí)行方法,thread info:%@",[NSThread currentThread]];
    NSMutableString *str = [NSMutableString stringWithString:info];
    for (NSInteger i = 0; i < 5; i++) {
        [str appendString:[NSString stringWithFormat:@"\n第%@次循環(huá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
{
    //線程執(zhí)行完成后會死掉,如果再次調用其start方法會crash
    //線程正在執(zhí)行中,如果再次調用其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的介紹就差不多完了,兩者在開發(fā)中用途不是很多,所以也沒有做特別深入的研究,例如線程順序執(zhí)行、線程同步等問題。這是本人寫的第一篇博客,肯定有不正確或者不恰當?shù)牡胤剑Mㄟ^以后更多的寫作實踐得以改善。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容