OC中常用的多線程編程技術: 1. NSThread NSThread是Objective-C中最基本的線程抽象,它允許程式員直接管理線程的生命周期。 NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selec ...
OC中常用的多線程編程技術:
1. NSThread
NSThread
是Objective-C中最基本的線程抽象,它允許程式員直接管理線程的生命周期。
NSThread *myThread = [[NSThread alloc] initWithTarget:self selector:@selector(myThreadMainMethod:) object:nil];
[myThread start];
使用NSThread
時,需要自己管理線程的生命周期,包括創建、啟動和銷毀線程。這種方法給了開發者很大的控制權,但也增加了複雜性,因為需要手動處理線程同步和線程安全問題。
2. Grand Central Dispatch (GCD)
GCD是一個強大的基於C語言的API,它提供了一個併發執行任務的低級別方式。GCD使用任務隊列和線程池來優化線程的使用。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// 非同步執行的任務
});
GCD是推薦的多線程編程方法之一,因為它的性能很好,而且簡化了併發編程的複雜性(下一節詳細介紹)。
3. Operation Queues
NSOperation
和NSOperationQueue
提供了一個面向對象的方式來執行併發操作。NSOperation
是一個抽象類,可以通過繼承它來定義具體的操作。
NSOperationQueue *queue = [[NSOperationQueue alloc] init];
NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(myTaskMethod) object:nil];
[queue addOperation:operation];
NSOperationQueue
可以管理多個NSOperation
對象,它支持設置最大併發操作數和依賴關係。NSOperation
比GCD更高級,它支持取消操作、設置操作依賴和觀察操作狀態。
4. Perform Selector Methods
Objective-C提供了performSelector:onThread:
等方法來在指定的線程上執行方法。
[self performSelector:@selector(myTaskMethod) onThread:myThread withObject:nil waitUntilDone:NO];
這些方法簡單易用,但它們不提供GCD和NSOperation
的強大功能。
5. POSIX Threads (pthreads)
POSIX線程是一套跨平臺的線程相關的API,Objective-C可以直接使用這些API,因為它是C語言的超集。
#include <pthread.h>
void *myThreadFunction(void *context) {
// 線程執行的代碼
return NULL;
}
pthread_t thread;
pthread_create(&thread, NULL, myThreadFunction, NULL);
pthreads
提供了很大的控制力,但它是一個低級別的API,通常不推薦在OC中使用,除非需要與C庫交互或有特殊的線程管理需求。
GCD的詳細介紹
Grand Central Dispatch (GCD) 是 Apple 開發的一個強大的多核編程解決方案,它提供了一種簡單且高效的方式來管理併發任務。GCD 使用任務(blocks of code)和隊列(queues)的概念來執行工作。它是基於 C 語言實現的,可以在 Objective-C 和 Swift 中使用。
核心概念
任務
任務是指要執行的工作,通常是以 block 的形式提供。在 Objective-C 中,一個任務可以是一個 block 或者一個函數。
隊列
隊列是一種特殊的數據結構,用於按順序存儲任務。GCD 提供了兩種類型的隊列:
- 串列隊列(Serial Queue):一次只執行一個任務。隊列中的任務按照添加的順序依次執行。在內部實現了一種機制,確保隊列中的任務一次只能執行一個,並且按照它們被添加到隊列中的順序來執行。這是通過隊列管理和任務調度來實現的。
當將一個任務提交到串列隊列時,這裡是大致的工作流程:
-
任務排隊:任務被添加到隊列的末尾。如果隊列是空的,它會成為隊列中的第一個任務。
-
任務執行:隊列中的第一個任務(隊頭)被取出來執行。在這個任務執行期間,隊列不會執行或者開始執行任何其他任務。
-
任務完成:一旦當前執行的任務完成,它會從隊列中移除。
-
下一個任務:隊列中的下一個任務(現在是隊頭)開始執行。
-
重覆過程:這個過程會一直重覆,直到隊列中的所有任務都被執行完畢。
串列隊列的關鍵特性是互斥執行,這意味著在任何給定時間點,隊列中只有一個任務在執行。這種互斥是由GCD的底層調度機制保證的,它確保了即使在多核處理器上,串列隊列上的任務也不會並行執行。
這種執行方式使得串列隊列成為了同步執行任務的理想選擇,特別是需要按順序執行一系列任務,而這些任務又不能同時執行時(例如,當任務需要按特定順序訪問或修改共用資源時)。因此理論上一個線程就足夠了。這是串列隊列如何保證任務順序執行和互斥的關鍵。當一個任務在串列隊列中開始執行時,它會持續運行直到完成,然後隊列才會執行下一個任務。這個過程不需要同時有多個線程參與,因為不會有並行執行的情況。然而,實際上,由於GCD的工作原理,它可能會在內部使用多個線程來管理多個串列隊列。GCD使用線程池來優化線程的使用,這意味著它會根據需要動態地為隊列分配和回收線程。但對於任何單一的串列隊列來說,你可以認為它在任何時候都只在一個線程上執行任務。這種設計使得串列隊列成為管理共用資源和避免併發問題的理想工具,因為它簡化了同步和線程安全的需求。同時,它也減少了上下文切換的開銷,因為任務是在單個線程上連續執行的。
- 並行隊列(Concurrent Queue):可以同時執行多個任務。任務可以併發執行,但完成的順序可能會不同。
系統隊列
GCD 提供了幾種不同類型的系統隊列:
- 主隊列(Main Queue):串列隊列,用於在主線程上執行任務,通常用於更新 UI。
- 全局隊列(Global Queues):並行隊列,有四個不同優先順序的全局隊列:高、預設、低和後臺。
Grand Central Dispatch (GCD) 提供了幾種不同類型的系統隊列,這些隊列是預先創建好的,可以直接使用。它們分為兩大類:主隊列(Main Queue)和全局隊列(Global Queues)。
主隊列(Main Queue)
- 主隊列是一個特殊的串列隊列,它在應用程式的主線程上執行任務。因為主線程通常用於更新UI,所以所有的UI更新都應該在主隊列上執行,以確保UI的平滑和響應性。
- 使用
dispatch_get_main_queue()
函數可以獲取主隊列。
dispatch_queue_t mainQueue = dispatch_get_main_queue();
dispatch_async(mainQueue, ^{
// 在主線程上更新UI
});
全局隊列(Global Queues)
- 全局隊列是並行隊列,它們在後臺執行任務,不會阻塞主線程。全局隊列有四個不同的優先順序:高(high)、預設(default)、低(low)和後臺(background)。這些優先順序對應於系統為任務分配的相對重要性。
- 使用
dispatch_get_global_queue()
函數可以獲取全局隊列,需要指定優先順序和一個保留用的標誌位(目前應該傳遞0
)。
dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(globalQueue, ^{
// 在後臺執行耗時任務
});
全局隊列的優先順序:
- 高優先順序(
DISPATCH_QUEUE_PRIORITY_HIGH
):用於需要立即執行的任務,但不應該阻塞主線程。 - 預設優先順序(
DISPATCH_QUEUE_PRIORITY_DEFAULT
):用於大多數任務,如果沒有特殊的優先順序要求,應該使用這個優先順序。 - 低優先順序(
DISPATCH_QUEUE_PRIORITY_LOW
):用於不急迫的任務,可以等待其他更重要的任務完成後再執行。 - 後臺優先順序(
DISPATCH_QUEUE_PRIORITY_BACKGROUND
):用於那些用戶不太可能立即註意到的任務,如預取數據、維護或清理工作。
註意以下事項:
- 儘管全局隊列是並行隊列,但是任務的啟動順序仍然是按照它們被添加到隊列的順序。
- 全局隊列不保證任務完成的順序,任務可以併發執行。
- 主隊列保證任務按照添加的順序一個接一個地執行。
- 在主隊列上同步執行任務會導致死鎖,因為主隊列等待同步任務完成,而同步任務又在等待主隊列可用,從而形成了相互等待的情況。
自定義隊列
除了系統隊列,GCD還允許創建自定義隊列。自定義隊列可以是串列的也可以是並行的。
- 創建串列隊列:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.mySerialQueue", DISPATCH_QUEUE_SERIAL);
- 創建並行隊列:
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.myConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
使用 GCD
非同步執行
使用 dispatch_async
函數可以非同步地將任務提交到隊列中。這意味著它不會等待任務完成,而是立即返回。
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(queue, ^{
// 執行耗時的任務
});
同步執行
使用 dispatch_sync
函數可以同步地將任務提交到隊列中。這會阻塞當前線程,直到任務執行完成。
dispatch_sync(queue, ^{
// 執行任務
});
延遲執行
使用 dispatch_after
函數可以在指定的時間後非同步執行任務。
double delayInSeconds = 2.0;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^{
// 2秒後執行的任務
});
一次性執行
使用 dispatch_once
函數可以確保代碼塊只被執行一次,常用於創建單例。
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只執行一次的代碼
});
隊列組
dispatch_group
允許多個任務作為一個組來提交,併在組中的所有任務完成時得到通知。
dispatch_group_t group = dispatch_group_create();
dispatch_group_async(group, queue, ^{
// 任務1
});
dispatch_group_async(group, queue, ^{
// 任務2
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 所有任務完成後執行
});
信號量
dispatch_semaphore
用於控制訪問資源的線程數量,可以用來實現線程同步。
dispatch_semaphore_t semaphore = dispatch_semaphore_create(1);
dispatch_async(queue, ^{
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 訪問受限資源
dispatch_semaphore_signal(semaphore);
});
註意事項
- 不要在串列隊列上同步地執行任務,這可能會導致死鎖。
- 儘量避免在主隊列上同步執行耗時任務,這會阻塞 UI 更新。
- 使用 GCD 時,要註意記憶體管理,特別是在 block 中捕獲外部變數時。