国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

iOS開發-多線程編程技術(Thread、Cocoaoperations、GCD)

2019-11-14 19:39:19
字體:
來源:轉載
供稿:網友

簡介

在軟件開發中,多線程編程技術被廣泛應用,相信多線程任務對我們來說已經不再陌生了。有了多線程技術,我們可以同做多個事情,而不是一個一個任務地進行。比如:前端和后臺作交互、大任務(需要耗費一定的時間和資源)等等。也就是說,我們可以使用線程把占據時間長的任務放到后臺中處理,而不影響到用戶的使用。

 

 

線程間通訊

有一個非常重要的隊列,就是主隊列。在這個隊列中處理多點觸控及所有與UI相關操作等等。它非常特殊,原因有兩點。一是我們絕對不想它阻塞,我們不會將需要執行很長時間的任務放在主隊列上執行。二是我們將其用于所有與UI相關的同步,也就是線程間通訊需要注意的地方。所有有可能會使屏幕UI發生變化的,都應放在主隊列上執行。

 

線程的定義:

每個正在系統上運行的程序都是一個進程。每個進程包含一到多個線程。進程也可能是整個程序或者是部分程序的動態執行。線程是一組指令的集合,或者是程序的特殊段,它可以在程序里獨立執行。也可以把它理解為代碼運行的上下文。所以線程基本上是輕量級的進程,它負責在單個程序里執行多任務。通常由操作系統負責多個線程的調度和執行。

轉自百度百科:多線程

 

如果熟悉多線程編程技術這一塊的朋友們,可以去看關于多線程安全的文章,是我寫的另一篇文章”iOS開發-多線程開發之線程安全篇“;

 

IOS支持的多線程技術:

一、Thread:

1)顯式創建線程:NSThreed

2)隱式創建線程:NSObject

二、Cocoa Operations:

NSOperation類是一個抽象類,因為我們必須使用它的兩個子類。

  1)NSInvocationOperation 

2)NSBlockOperation

————————————————————————————

3)NSOperationQueue(繼承于NSObject)

三、Grand Central Dispatch (GCD):

1)GCD的創建

2)重復執行線程及一次性執行:dispatch_apply & dispatch_once

3)操作(串行)隊列:dispatch_queue_create

4)GCD群組通知:dispatch_group_t

5)GCD實現計時器

6)后臺運行

7)延遲執行

四、比較多線程技術

 

 

一、Thread

我們可以使用NSTherad或NSObject類去調用:

1)顯式創建線程:NSThread

創建NSThread有兩個辦法

1.1)創建之后需要使用start方法,才會執行方法:

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

 

1.2)創建并馬上執行方法:

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

 

2)隱式創建線程:NSObject

我們也可以使用NSObject類的方法直接調用方法

[self performSelectorInBackground:@selector(threadAlloc) withObject:nil];

 

取消線程的方法:

實際上并沒有真正提供取消線程的API。蘋果提供了一個cancel的api,但它不能作用于取消線程,它只能改變線程的運行狀態。我們可以使用它來進行條件判斷。

- (void)threadCancel{    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(threadCancelNow) object:nil];    [thread start];}- (void)threadCancelNow{    int a = 0;    while (![[NSThread currentThread] isCancelled]) {        NSLog(@"a - %d", a);        a++;        if (a == 5000) {            NSLog(@"終止循環");            [[NSThread currentThread] cancel];            break;        }    }}

程序效果:循環輸出5000次,線程就會被終止。

 

NSThread線程間通訊-調用主線程修改UI:

只需要傳遞一個selector和它的參數,withObject參數可以為nil,waitUntilDone代表是否要等待調用它的這個線程執行之后再將它從主隊列調出,并在主隊列上運行,通常設為NO,不需要等待。

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

 

NSThread相關屬性及方法:

// 獲取/設置線程的名字@PRoperty (copy) NSString *name NS_AVAILABLE(10_5, 2_0);/** *  獲取當前線程的線程對象 * *  通過這個屬性可以查看當前線程是第幾條線程,主線程為1。 *  可以看到當前線程的序號及名字,主線程的序號為1,依次疊加。 */+ (NSThread *)currentThread;// 線程休眠(秒)+ (void)sleepForTimeInterval:(NSTimeInterval)ti;// 線程休眠,指定具體什么時間休眠+ (void)sleepUntilDate:(NSDate *)date;// 退出線程 // 注意:這里會把線程對象銷毀!銷毀后就不能再次啟動線程,否則程序會崩潰。+ (void)exit;

 

 

二、Cocoa operations

1)NSInvocationOperation

創建NSInvocationOperation線程,附帶一個NSString參數:

NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction:) object:@"abc"];// 需要啟動線程,默認是不啟動的。[operation start];

如在創建時定義了參數,那么接收的時候,可以對sender進行轉換,如字符串、數組等:

- (void)invocationAction:(NSInvocationOperation *)sender{    NSLog(@"sender - %@", sender);      // 輸出params    NSString *str = (NSString *)sender;    NSLog(@"str - %@e", str);           // params}

附帶一提,線程的普通創建一般為并發執行的,因為串行隊列是需要顯式創建的,如沒看見此類代碼,那么即是并發隊列線程,因此,上述代碼也就是并發線程。關于并發和串行隊列(線程),我將會在下面詳細說明,我們繼續往下看。

 

你也可以使用NSOperationQueue來創建一個線程隊列,用來添加子線程:

NSOperationQueue *invocationQueue = [[NSOperationQueue alloc] init];// 線程ANSInvocationOperation *invocationQ1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction:) object:@"invocationQ1"];// 線程BNSInvocationOperation *invocationQ2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(invocationAction:) object:@"invocationQ2"];// 往invocationQueue添加子線程[invocationQueue addOperations:@[invocationQ1, invocationQ2] waitUntilFinished:YES];

必須使用addOperations:方法把線程添加至隊列,不然線程不會執行,隊列是并行執行。或者,你也可以使用addOperation:方法添加單個線程。

 

2)NSBlockOperation

 

創建NSBlockOperation

// 創建線程任務NSBlockOperation *blockOperation = [NSBlockOperation                                    blockOperationWithBlock:^{                                        [NSThread sleepForTimeInterval:2];                                        NSLog(@"one - %@", [NSThread currentThread]);                                    }];;// 執行線程任務[blockOperation start];

 

注意:這會在當前的線程中執行,因為它是根據調用的線程所決定的。

 

比方說你在主線程中運行它,那么它就是在主線程中執行任務。如果你是在子線程中運行它,那么它就是在子線程中執行任務。

 

做個簡單的實驗,我們新建一條子線程,然后在子線程里調用NSBlockOperation

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSBlockOperation *blockOperation = [NSBlockOperation                                        blockOperationWithBlock:^{                                            [NSThread sleepForTimeInterval:2];                                            NSLog(@"one - %@", [NSThread currentThread]);                                            // print: one - <NSThread: 0x7f8ac2e1d0b0>{number = 2, name = (null)}                                        }];;        [blockOperation start];});

 

它將打印:one - <NSThread: 0x7f8ac2e1d0b0>{number = 2, name = (null)},因此這個理論是正確的

 

我們也可以使它并發執行,通過使用addExecutionBlock方法添加多個Block,這樣就能使它在主線程和其它子線程中工作。

NSBlockOperation *blockOperation = [NSBlockOperation                                    blockOperationWithBlock:^{                                        NSLog(@"one - %@", [NSThread currentThread]);                                    }];;[blockOperation addExecutionBlock:^{    NSLog(@"two - %@", [NSThread currentThread]);}];[blockOperation addExecutionBlock:^{    NSLog(@"three - %@", [NSThread currentThread]);}];[blockOperation addExecutionBlock:^{    NSLog(@"four - %@", [NSThread currentThread]);}];[blockOperation start];

 

它將打印:

two - <NSThread: 0x7fea8a70b000>{number = 3, name = (null)}one - <NSThread: 0x7fea8a558a40>{number = 4, name = (null)}four - <NSThread: 0x7fea8a406b90>{number = 1, name = main}three - <NSThread: 0x7fea8a436e40>{number = 2, name = (null)}

 

大家都看到,即使我們通過使用addExecutionBlock方法使它并發執行任務,但是它也依舊會在主線程執行,因此我們就需要使用NSOperationQueue了。

 

3)NSOperationQueue

 

這里介紹一下NSOperation的依賴關系,依賴關系會影響線程的執行順序:

// 創建操作隊列NSOperationQueue *queue = [[NSOperationQueue alloc] init];// 線程ANSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{    NSLog(@"op1");    [NSThread sleepForTimeInterval:2];}];// 線程BNSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{    NSLog(@"op2");}];// 線程CNSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{    NSLog(@"op3");    [NSThread sleepForTimeInterval:2];}];// 線程B依賴線程C,也就是等線程C執行完之后才會執行線程B[op2 addDependency:op3];// 線程C依賴線程A,同上,只不過依賴對象改成了線程A[op3 addDependency:op1];// 為隊列添加線程[queue addOperation:op1];[queue addOperation:op2];[queue addOperation:op3];

當你沒添加依賴時,隊列是并行執行的。

注意:依賴關系可以多重依賴,但不要建立循環依賴。

 

Cocoa operations線程間通信-調用主線程修改UI:

// 創建線程對象(并發)NSBlockOperation *blockOperation = [[NSBlockOperation alloc] init];// 添加新的操作[blockOperation addExecutionBlock:^{    NSLog(@"two - %@", [NSThread currentThread]);}];// 添加新的操作[blockOperation addExecutionBlock:^{    NSLog(@"three - %@", [NSThread currentThread]);    // 在主線程修改UI    NSOperationQueue *queue = [NSOperationQueue mainQueue];    [queue addOperationWithBlock:^{        [self editUINow];    }];}];[blockOperation start];

 

 NSOperation方法及屬性:

// 設置線程的最大并發數@property NSInteger maxConcurrentOperationCount;// 線程完成后調用的Block@property (copy) void (^completionBlock)(void);// 取消線程- (void)cancel;

只列舉上面那些,其它的方法就不全列出來了。

 

注意:在NSOperationQueue類中,我們可以使用cancelAllOperations方法取消所有的線程。這里需要說明一下,不是執行cancelAllOperations方法時就會馬上取消,是等當前隊列執行完,下面的隊列不會再執行。

 

三、Grand Central Dispatch (GCD)

1)GCD異步線程的創建:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSLog(@"線程 - %@", [NSThread currentThread]);});

GCD也可以創建同步的線程,只需要把async改成sync即可。

 

2)重復執行線程:dispatch_apply

以下代碼會執行4次:

dispatch_apply(4, DISPATCH_QUEUE_PRIORITY_DEFAULT, ^(size_t index) {    // index則為執行的次數 0開始遞增    NSLog(@"one - %ld", index);});

index參數為執行的次數,從0開始遞增。

 

其中需要注意的是,每次執行都會新開辟一條子線程,因為是異步的原因,它們不會是順序的。

[657:159159] one - 0, thread - <NSThread: 0x100110b50>{number = 1, name = main}[657:159191] one - 2, thread - <NSThread: 0x103800000>{number = 2, name = (null)}[657:159192] one - 3, thread - <NSThread: 0x100112b90>{number = 3, name = (null)}[657:159190] one - 1, thread - <NSThread: 0x100501180>{number = 4, name = (null)}

 

 

然而,GCD還有一次性執行的方法,如下所示:

dispatch_once_t once;dispatch_once(&once, ^{    NSLog(@"once - %@", [NSThread currentThread]); // 主線程});

以上代碼執行一次,如果你把它放到循環的地方,運行時就會崩潰。

 

3)操作隊列:dispatch_queue_create

使用GCD也能創建串行隊列,具體代碼如下:

/** *  GCD創建串行隊列 * *  @param "com.GarveyCalvin.queue"  隊列字符串標識 *  @param DISPATCH_QUEUE_CONCURRENT 可選的,可以是NULL * *  @return dispatch_queue_t */dispatch_queue_t queue = dispatch_queue_create("com.GarveyCalvin.queue", DISPATCH_QUEUE_CONCURRENT);// 線程Adispatch_async(queue, ^{    [NSThread sleepForTimeInterval:1];    NSLog(@"sleep async - %@", [NSThread currentThread]);});// 線程Bdispatch_barrier_async(queue, ^{    [NSThread sleepForTimeInterval:3];    NSLog(@"sleep barrier2 - %@", [NSThread currentThread]);});// 線程Cdispatch_async(queue, ^{    NSLog(@"async");});

運行效果:以上會先執行 線程A-》線程B-》線程C,它是一個串行隊列。

dispatch_queue_create的第二個參數:

1)DISPATCH_QUEUE_SERIAL(串行)

2)DISPATCH_QUEUE_CONCURRENT(并發)

 

4)GCD群組通知:dispatch_group_t

GCD的高級用法,等所有線程都完成工作后,再作通知。

// 創建群組dispatch_group_t group = dispatch_group_create();// 線程Adispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSLog(@"group1");    [NSThread sleepForTimeInterval:2];});// 線程Bdispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSLog(@"group2");});// 待群組里的線程都完成之后調用的通知dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSLog(@"group success");});

群組里的線程也是并行隊列。線程A和線程B都執行完之后,會調用通知打印group success。

 

5)GCD實現計時器

__block int time = 30;CGFloat reSecond = 1.0;dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, reSecond * NSEC_PER_SEC, 0);dispatch_source_set_event_handler(timer, ^{    time--;    NSLog(@"%d", time);    if (time == 0) {        dispatch_source_cancel(timer);    }});dispatch_resume(timer);

代碼效果:創建了一個計時器,計時器運行30秒,每過一秒會調用一次block,我們可以在block里面寫代碼。因為dispatch_source_t默認是掛起狀態,因此我們使用時需要使用dispatch_resume方法先恢復,不然線程不會執行。

 

GCD線程間通信-調用主線程修改UI:

有時候我們請求后臺作數據處理,數據處理是異步的,數據處理完成后需要更新UI,這時候我們需要切換到主線程修改UI,例子如下:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{    NSLog(@"異步數據處理 - %@", [NSThread currentThread]);    [NSThread sleepForTimeInterval:2];    NSLog(@"數據處理完成");        // 調用主線程更新UI    dispatch_async(dispatch_get_main_queue(), ^{        NSLog(@"更新UI - %@", [NSThread currentThread]);        [self editUINow];    });});

因為是在主線程修改UI,所以我們最好是使用同步的GCD方法dispatch_sync。但這還不夠,我們還需要使用dispatch_get_main_queue()方法來獲得主線程,之后就是作UI的更新工作了。

 

GCD方法及屬性:

// 獲取主線程dispatch_get_main_queue()// 創建隊列:第一個參數是隊列的名稱,它會出現在調試程序等之中,是個內部名稱。第二個參數代表它是串行隊列還是并并發隊列,NULL代表串行隊列。dispatch_queue_t dispatch_queue_create(const char *label, dispatch_queue_attr_t attr);// 創建異步調度隊列void dispatch_async(dispatch_queue_t queue, dispatch_block_t block);// 恢復隊列void dispatch_resume(dispatch_object_t object);// 暫停隊列void dispatch_suspend(dispatch_object_t object);

小結:本文主要介紹了IOS三種線程對比及其使用方法。需要特別注意的是,在修改任何有關于UI的東西,我們必須要切換至主線程,在主線程里修改UI,避免不必要的麻煩產生。蘋果是推薦我們使用GCD,因為GCD是這三種里面抽象級最高的,使用起來也簡單,也是消耗資源最低的,并且它執行效率比其它兩種都高。因此,能夠使用GCD的地方,盡量使用GCD。

 

6)后臺運行

使用block的另一個好處是可以讓程序在后臺較久地運行。在以前,當應用被按Home鍵退出后,應用僅有最多5秒的時間做一些保存或清理資源的工作。 但是如果使用GCD,你可以讓你的應用最多有10分鐘的時間在后臺長久運行。這個時間可以用來做各種事情,包括清理本地緩存、發送統計數據等工作。

AppDelegate.h@interface AppDelegate ()@property (assign, nonatomic) UIBackgroundTaskIdentifier backGroundUpdate;@endAppDelegate.m- (void)applicationDidEnterBackground:(UIApplication *)application {    [self beginBackGroundUpdate];   // 需要長久運行的代碼    [self endBackGroundUpdate];}- (void)beginBackGroundUpdate{    self.backGroundUpdate = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{        [self endBackGroundUpdate];    }];}- (void)endBackGroundUpdate{    [[UIApplication sharedApplication] endBackgroundTask:self.backGroundUpdate];    self.backGroundUpdate = UIBackgroundTaskInvalid;}

建議大家在真機上測試,因為筆者在模擬器測試了24分鐘還有效。

 

7)延遲執行

如果我們想要某段代碼延遲執行,那么可以使用dispatch_after ,但是有一個缺點是,當提交代碼后(代碼執行后),我們不能取消它,它將會運行。另外,我們可以使用 NSTimer 進行延時操作,值得一提,它是可以被取消的。

dispatch_time_t time_t = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(time * NSEC_PER_SEC));dispatch_queue_t queue = dispatch_get_main_queue();dispatch_after(time_t, queue, ^{    NSLog(@"hahalo");});

 

 

 

比較多線程技術

一、Thread: 

優點:量級較輕。

缺點:需要自己管理線程的生命周期,線程同步。線程同步對數據的加鎖會有一定的系統開銷。

二、Cocoa operations:

優點:不需要關心線程管理,數據同步的事情,可以把精力放在自己需要執行的操作上。

三、Grand Central Dispatch (GCD):

優點:GCD基于C的API,非常底層,可以充分利用多核,能夠輕松在多核系統上高效運行并發代碼,也是蘋果推薦使用的多線程技術。

 

 

 

本文參考:

iOS多線程開發

GCD的另一個用處是可以讓程序在后臺較長久的運行。

全面掌握iOS多線程攻略 —— PS:這個攻略較多,但是有很多重復的內容。

iOS多線程的初步研究(一)-- NSThread

 

 


博文作者:GarveyCalvin

博文出處:http://m.survivalescaperooms.com/GarveyCalvin/

本文版權歸作者和博客園共有,歡迎轉載,但須保留此段聲明,并給出原文鏈接,謝謝合作!

 


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 漳平市| 姜堰市| 天峨县| 宁津县| 南丹县| 太和县| 隆尧县| 靖安县| 贡觉县| 华容县| 曲麻莱县| 宜章县| 永德县| 蓬溪县| 来宾市| 漠河县| 赤城县| 屯门区| 麻栗坡县| 修文县| 萍乡市| 长武县| 广水市| 拜城县| 邳州市| 新泰市| 太和县| 凤冈县| 贺兰县| 岫岩| 威宁| 阜新| 雷山县| 桂林市| 荆门市| 深泽县| 齐河县| 黔西县| 紫云| 苗栗县| 卢龙县|