天天看點

ios學習--多線程技術

程序與線程概念

--一個運作的程式就是一個程序或者叫做一個任務

--一個程序至少包含一個線程,線程是程式的執行流

--iOS程式啟動時,在建立一個程序的同時, 會開始運作一個線程,該線程被稱為主線程

--主線程是其他線程最終的父線程,所有界面的顯示操作必須在主線程進行!!!

--系統中的每一個程序都有自己獨立的虛拟記憶體空間,而同一個程序中的多個線程則共用程序的記憶體空間

--每建立一個新的線程,都會消耗一定記憶體和CPU時間

--當多個線程對同一個資源出現争奪的時候需要注意線程安全問題

多線程的優勢與難點

--優勢

*充分發揮多核處理器優勢,将不同線程任務配置設定給不同的處理器,真正進入“并行運算”狀态

*将耗時、輪詢或者并發需求高等任務配置設定到其他線程執行,并由主線程負責統一更新界面會使得應用程式更加流暢,使用者體驗更好

*當硬體處理器的數量增加,程式會運作更快,而無需做任何調整

--難點

*共享資源的“争奪”

*多線程是為了同步完成多項任務,不是為了提高運作效率,而是為了通過提高資源使用效率來提高系統的整體性能

多線程使用注意事項

--線程使用不是無節制的

*iOS中的主線程的堆棧大小是1M

*從第二個線程開始都是512KB

*這些數值不能通過編譯器開關或線程API函數更改

--隻有主線程有直接修改UI的能力

iOS的三種多線程技術

1.NSThread

2.NSOperation

3.GCD —— Grand Central Dispatch,是基于C語言的架構

以上這三種程式設計方式從上到下,抽象度層次是從低到高的,抽象度越高的使用越簡單,也是Apple最推薦使用的。但是就目前而言,iOS的開發者,需要了解三種多線程技術的基本使用過程。因為很多架構技術分别使用了不同多線程技術。例如NSURLConnection的異步靜态方法:

sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *error)

三種多線程技術的對比

NSThread:

優點:NSThread 比其他兩個輕量級,使用簡單

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

NSOperation:

不需要關心線程管理,資料同步的事情,可以把精力放在自己需要執行的操作上

GCD:

Grand Central Dispatch是由蘋果開發的一個多核程式設計的解決方案。iOS4.0+才能使用,是替代NSThread, NSOperation的高效和強大的技術

NSThread

建立線程方法:

+ (void)detachNewThreadSelector:(SEL)selector toTarget:(id)target withObject:(id)argument;

- (id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument;

參數說明:

--selector:線程執行的方法,隻能有一個參數,不能有傳回值

--target:selector消息發送的對象

--argument:傳輸給target的唯一參數,也可以是nil

NSObject直接加入了多線程的支援,允許對象的某個方法在背景運作(本方法普遍應用在遊戲中,利用多線程播放聲音)

- (void)performSelectorInBackground:(SEL)aSelector withObject:(id)arg;

代碼演練

// 建立線程1

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

// 設定線程名稱,因為需要通過線程名稱跟蹤線程執行情況,是以此處不使用線程靜态方法

[thread1 setName:@"售票線程-1"];

// 啟動線程1

[thread1 start];

// 建立線程2,步驟同上

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

[thread2 setName:@"售票線程-2"];

[thread2 start];

NSThread使用注意

*當涉及到共享資源争奪時,共享資源的資料加鎖是一個難點,既要保證資料安全,又要保證線程執行效率

--使用前加鎖

--盡快使用

--使用完解鎖

--再去做其他的事情

*多線程編寫順序

1.單個方法調試OK

2.單個線程調試OK

3.增加線程,并考慮線程加鎖、解鎖的準确位置。

注意:

--隻有主線程能夠修改UI

--如果不涉及到記憶體争搶,NSThread寫多線程是最簡單的

*當不涉及共享資源争奪時,使用NSObject的performSelectorInBackground方法可以非常的友善地實作多線程

NSOperation & NSOperationQueue

*NSOperation的兩個子類

1.NSInvocationOperation

2.NSBlockOperation

*工作原理:

--用NSOperation封裝要執行的操作

--将建立好的NSOperation對象放NSOperationQueue中

--啟動OperationQueue開始新的線程執行隊列中的操作

*注意事項:

--使用多線程時通常需要控制線程的并發數,因為線程會消耗系統資源,同時運作的線程過多,系統會變慢

--使用以下方法可以控制并發的線程數量:

- (void)setMaxConcurrentOperationCount:(NSInteger)cnt;

NSOperation操作流程

1.定義操作

2.定義隊列

3.将操作添加至隊列,隊列是自動啟動的

代碼演練

while (YES) {

    if (_tickets > 0) {

        // 在主線程操作隊列更新界面

        [[NSOperationQueue mainQueue]addOperationWithBlock:^{

            NSString *str = [NSString stringWithFormat:@"目前票數:%d,售票線程:%@", _tickets, operationName];

            [self appendTextView:str];

            _tickets--;

        }];

        // 模拟休息

        // ……

    } else {

        // 在主線程操作隊列更新界面

        [[NSOperationQueue mainQueue]addOperationWithBlock:^{

            NSString *str = [NSString stringWithFormat:@"票已售完,售票線程:%@", operationName];

            [self appendTextView:str];

        }];

        break;

    }

}

******NSInvocationOperation代碼*********

// 設定預售票數,共享資源!

_tickets = 20;

// 定義操作1

NSInvocationOperation *operation1 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票操作-1"];

// 定義操作2

NSInvocationOperation *operation2 = [[NSInvocationOperation alloc]initWithTarget:self selector:@selector(operationSaleMethod:) object:@"售票操作-2"];

// 定義操作隊列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

[queue addOperation:operation1];

[queue addOperation:operation2];

******NSBlockOperation代碼*********

// 設定預售票數,共享資源!

_tickets = 20;

// 定義操作隊列

NSOperationQueue *queue = [[NSOperationQueue alloc]init];

[queue addOperationWithBlock:^{

    [self operationSaleMethod:@"售票操作-1"];

}];

[queue addOperationWithBlock:^{

    [self operationSaleMethod:@"售票操作-2"];

}];

NSOperation使用注意

--NSOperation中無需使用線程鎖

--除更新UI之外,對共享資源的争奪也需放在主線程隊列之中

--将定義的操作添加至隊列之後,多線程便開始啟動

--NSBlockOperation的使用相比NSInvocationOperation更加靈活、友善

--通過setMaxConcurrentOperationCount方法可以控制并發的最大線程數量

GCD

*GCD是基于C語言的架構

*工作原理:

--讓程式平行排隊的特定任務,根據可用的處理資源,安排它們在任何可用的處理器上執行任務

--要執行的任務可以是一個函數或者一個block

--底層是通過線程實作的,不過程式員可以不必關注實作的細節

--GCD中的FIFO隊列稱為dispatch queue,可以保證先進來的任務先得到執行

dispatch_notify可以實作監聽一組任務是否完成,完成後得到通知

*dispatch queue:

--Main dispatch queue:是全局可用的隊列,用于在主線程上執行任務

--Serial:同時隻執行一個任務

--Concurrent:可以并發地執行多個任務,但是執行完成的順序是随機的

GCD使用流程

1. 擷取全局排程隊列

2. 建立排程群組

3. 向排程群組添加異步任務,并指定執行隊列

4. 接收群組排程完成通知,群組中所有任務完成後獲得通知

代碼演練

while (YES) {

    if (_tickets > 0) {

        // 在主排程隊列更新界面

            dispatch_async(dispatch_get_main_queue(), ^{

            NSString *str = [NSString stringWithFormat:@"目前票數:%d,售票線程:%@", _tickets, gcdName];

            [self appendTextView:str];

            _tickets--;

        });

        // 模拟休息

            if ([gcdName isEqualToString:@"售票GCD-1"]) {

            [NSThread sleepForTimeInterval:0.1f];

        } else {

            [NSThread sleepForTimeInterval:0.2f];

        }

    } else {

        break;

    }

}

// 設定預售票數,共享資源!

_tickets = 20;

// 1. 擷取預設排程優先級的全局排程隊列,第二個參數為今後拓展使用,目前始終傳入0

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

// 2. 建立任務排程組

dispatch_group_t group = dispatch_group_create();

// 3. 排程群組異步任務

dispatch_group_async(group, queue, ^{

    [self gcdSaleMethod:@"售票GCD-1"];

});

dispatch_group_async(group, queue, ^{

    [self gcdSaleMethod:@"售票GCD-2"];

});

// 4. 接收群組排程通知

dispatch_group_notify(group, dispatch_get_main_queue(), ^{

    [self appendTextView:@"票已售完"];

});

GCD使用注意

--GCD的原理和Operation非常相像,隻是它是C語言架構的。

--GCD中無需使用線程鎖

--除更新UI之外,對共享資源的争奪也需放在主排程隊列之中

--将任務添加至群組,并指定全局操作隊列,使得GCD的多線程更加靈活、友善

--dispatch_group_notify可以監聽一組任務是否完成。這個方法很有用,比如你執行三個下載下傳任務,當三個任務都下載下傳完成後,才通知界面說已經完成

--如果不需要監聽一組任務,可以直接使用dispatch_async方法

ios學習--多線程技術