Concurrent Programming: APIs and Challenges - objc.io issue #2 的整理筆記
難 -> 易 : pthread、NSThread、GCD、NSOperationQueue
使用 GCD
預設有五個 queue
- main queue
- 3 個不同 priority queue
- I/O queue
大多數情況使用 default 的 priority queue 就好,避免 priority inversion
使用 Operation Queues
可透過 override main or start 定義自己的 operations。
重寫 main 的方式當 return 時,這 operation 就結束了
@implementation YourOperation - (void)main { } @end
|
更多處理或者要可以 async,這情況下需要手動管理狀態,使用預設的 setter 才會發送 KVO,否則需要自己發送。
@implementation YourOperation - (void)start { self.isExecuting = YES; self.isFinished = NO; }
- (void)finished { self.isExecuting = NO; self.isFinished = YES; } @end
|
為了使用 cancel,需一直檢查 isCancelled 屬性。
- (void)main { while (notDone && !self.isCancelled) { } }
|
將 operation 放到 queue 中
NSOperationQueue *queue = [[NSOperationQueue alloc] init]; YourOperation *operation = [[YourOperation alloc] init]; [queue addOperation:operation];
|
也可以直接將 block 放到 queue 中,但定義自己的 NSOperation 會比較好 debug。
[[NSOperationQueue mainQueue] addOperationWithBlock:^{ }];
|
NSOperation 還可以透過 maxConcurrentOperationCount
控制同時執行的數量,還有根據 queue 中的 operation 的 priority 排序,還可以在 operation 之間設 dependency。
而性能雖然比 GCD 要低一點,但大多數可以忽略不計。
使用 NSThread
使用這的問題是在我們的 code 中也做了建立 thread 的事情,可能導致 threads 爆炸。
FindMinMaxThread.h@interface FindMinMaxThread : NSThread @property (nonatomic) NSUInteger min; @property (nonatomic) NSUInteger max; - (instancetype)initWithNumbers:(NSArray *)numbers; @end
@implementation FindMinMaxThread { NSArray *_numbers; }
- (instancetype)initWithNumbers:(NSArray *)numbers { self = [super init]; if (self) { _numbers = numbers; } return self; }
- (void)main { NSUInteger min; NSUInteger max; self.min = min; self.max = max; } @end
|
呼叫的方式
NSMutableSet *threads = [NSMutableSet set]; NSUInteger numberCount = self.numbers.count; NSUInteger threadCount = 4;
for (NSUInteger i = 0; i < threadCount; i++) { NSUInteger offset = (count / threadCount) * i; NSUInteger count = MIN(numberCount - offset, numberCount / threadCount); NSRange range = NSMakeRange(offset, count); NSArray *subset = [self.numbers subarrayWithRange:range]; FindMinMaxThread *thread = [[FindMinMaxThread alloc] initWithNumbers:subset]; [threads addObject:thread]; [thread start]; }
|
使用 pthread
複雜不易使用
#import <pthread.h>
struct threadInfo { uint32_t * inputValues; size_t count; };
struct threadResult { uint32_t min; uint32_t max; };
void * findMinAndMax(void *arg) { struct threadInfo const * const info = (struct threadInfo *) arg; uint32_t min = UINT32_MAX; uint32_t max = 0; for (size_t i = 0; i < info->count; ++i) { uint32_t v = info->inputValues[i]; min = MIN(min, v); max = MAX(max, v); } free(arg); struct threadResult * const result = (struct threadResult *) malloc(sizeof(*result)); result->min = min; result->max = max; return result; }
int main(int argc, const char * argv[]) { size_t const count = 1000000; uint32_t inputValues[count];
for (size_t i = 0; i < count; ++i) { inputValues[i] = arc4random(); }
size_t const threadCount = 4; pthread_t tid[threadCount]; for (size_t i = 0; i < threadCount; ++i) { struct threadInfo * const info = (struct threadInfo *) malloc(sizeof(*info)); size_t offset = (count / threadCount) * i; info->inputValues = inputValues + offset; info->count = MIN(count - offset, count / threadCount); int err = pthread_create(tid + i, NULL, &findMinAndMax, info); NSCAssert(err == 0, @"pthread_create() failed: %d", err); } struct threadResult * results[threadCount]; for (size_t i = 0; i < threadCount; ++i) { int err = pthread_join(tid[i], (void **) &(results[i])); NSCAssert(err == 0, @"pthread_join() failed: %d", err); } uint32_t min = UINT32_MAX; uint32_t max = 0; for (size_t i = 0; i < threadCount; ++i) { min = MIN(min, results[i]->min); max = MAX(max, results[i]->max); free(results[i]); results[i] = NULL; }
NSLog(@"min = %u", min); NSLog(@"max = %u", max); return 0; }
|