你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> IOS多線程編程簡介

IOS多線程編程簡介

編輯:IOS開發基礎

基本概念

線程: 線程是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以並發多個線程,每條線程並行執行不同的任務。線程是獨立調度和分派的基本單位。同一進程中的多條線程將共享該進程中的全部系統資源,但是自有調用堆棧和寄存器環境。
進程: 進程是計算機中已運行程序的實體。其本身並不是幾部運行單位,是線程的容器。
任務: 任務(task)用於指代抽象的概念,表示需要執行工作,具體可以是一個函數或者一個block。

關於進程和線程的描述推薦《進程與線程的一個簡單解釋》,淺顯易懂。

IOS常用的多線程編程技術

IOS常用的多線程編程技術包括:NSThread、Cocoa NSOperation、GCD(grand central dispatch)。其優缺點對比如下表所示:

多線程技術優點缺點NSThread輕量級最低,相對簡單需要手動管理所有的線程活動(生命周期,休眠,同步等)線程同步對數據的加鎖會有一定的系統開銷Cocoa NSOperation自帶線程周期管理,可只關注自己處理邏輯NSOperation是面向對象的抽象類,實現只能是其子類(NSInvocationOperation和NSBlockOperation),對象需要添加到NSOperationQueue隊列裡執行GCD效率高,可避免並發陷阱基於C而非OC實現

如果對性能效率要求較高,處理大量並發時用GCD,簡單而安全的可用NSOPeration或者NSThread。Apple推薦使用GCD

NSThread

NSThread具體的底層實現機制是Mach線程,實現技術有三種Cocoa threads、POSIX threads(UNIX)、Multiprocessing Services。

使用方式

  1. 顯式創建方式:

    -(id)initWithTarget:(id)target selector:(SEL)selector object:(id)argument 
    +(void)detachNewThreadSelector:(SEL)aSelector toTarget:(id)aTarget withObject:(id)anArgument
  2. 隱式創建方式:

    [Object performSelectorInBackground:@selector(doSomething)withObject:nil];

    代碼示例

- (void)testNSThread{
    _lock = [[NSLock alloc] init];
    _condition = [[NSCondition alloc] init];
    self.total = 20;

    NSThread *thread1 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [thread1 setName:@"Thread--1"];
    [thread1 start];

    NSThread *thread2 = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
    [thread2 setName:@"Thread--2"];
    [thread2 start];

//    [NSThread detachNewThreadSelector:@selector(run) toTarget:self withObject:nil];
//    [self performSelectorInBackground:@selector(run) withObject:nil];
}


- (void)run{
    while (YES) {
        [self.lock lock];
        if(self.total >= 0){
            [NSThread sleepForTimeInterval:0.09];
            NSInteger count = 20 - self.total;
            NSLog(@"total is :%zd,left is:%zd, thread name is:%@", self.total, count,[[NSThread currentThread] name]);
            self.total--;
        }else{
            break;
        }
        [self.lock unlock];
    }
}

分析說明:

NSThread中線程內存管理,是否循環引用,同步問題都需要手動管理。除了代碼中的lock還可以用@synchronized來簡化NSLock的使用。

- (void)doSomeThing:(id)anObj 
{ 
    @synchronized(anObj) 
    { 
        // Everything between the braces is protected by the @synchronized directive. 
    } 
}

還可以用NSCondition中的wait消息設置等待線程,然後通過signal消息 發送信號的方式,在一個線程喚醒另外一個線程的等待。
線程間通信:

//在指定線程上執行操作
//在主線程上執行操作
[self performSelectorOnMainThread:@selector(run) withObject:nil waitUntilDone:YES]; 
[self performSelector:@selector(run) onThread:threadName withObject:nil waitUntilDone:YES]; 
//在當前線程執行操作
[self performSelector:@selector(run) withObject:nil];

獲取線程:

NSThread *current = [NSThread currentThread];
NSThread *main = [NSThread mainThread];

Cocoa NSOperation

使用方式

NSOperation的使用方式有兩種:

  • 使用NSInvocationOperation和NSBlockOperation

  • 自定義NSOperation子類。

    代碼示例

-(void)testNSOPeration{
    self.total = 20;
    NSInvocationOperation *operation1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run2) object:nil];
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    [queue addOperation:operation1];


    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        [self run2];
    }];
//    [operation1 start];//當前線程直接執行
    [operation2 addDependency:operation1];
//    [operation2 addExecutionBlock:^{
//        [self run];
//    }];
    [queue addOperation:operation2];
//    queue.maxConcurrentOperationCount = 1;
}

- (void)run2{
    while (YES) {
        if(self.total >= 0){
//            [NSThread sleepForTimeInterval:0.09];
            NSInteger count = 20 - self.total;
            NSLog(@"total is :%zd,left is:%zd, thread name is:%@", self.total, count,[[NSThread currentThread] name]);
            self.total--;
        }else{
            break;
        }
    }
}

自定義NSOperation子類
自定義NSOperation子類需要實現start、main、isExecuting、isFinish、isConcurrnet(asynchronous)方法。

@interface MyOperation : NSOperation {
    BOOL        executing;
    BOOL        finished;
}
- (void)completeOperation;
@end

@implementation MyOperation
- (id)init {
    self = [super init];
    if (self) {
        executing = NO;
        finished = NO;
    }
    return self;
}

- (BOOL)isConcurrent {
    return YES;
}

- (BOOL)isExecuting {
    return executing;
}

- (BOOL)isFinished {
    return finished;
}
@end

分析說明

NSOperationQueue 是一個並行隊列,可以通過設置maxConcurrentOperationCount來設定最大並行操作數,對於自定義實現的NSOperation子類,可以通過實現isConcurrent(isAsynchronous)方法來制定具體的操作是否同步執行。

GCD

使用方式

GCD的工作原理是讓程序平行排隊的特定任務,根據可用的處理資源,安排他們在任何可用的處理器核心上執行任務。
隊列的創建

  • 主線程隊列(串行):
    dispatch_get_main_queue()

  • 全局隊列(並行):
    dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
    第一個參數說明隊列的優先級,第二個參數暫未用到,默認0。根據優先級劃分,共有四個全局隊列

    #define DISPATCH_QUEUE_PRIORITY_HIGH 2
    #define DISPATCH_QUEUE_PRIORITY_DEFAULT 0
    #define DISPATCH_QUEUE_PRIORITY_LOW (-2)
    #define DISPATCH_QUEUE_PRIORITY_BACKGROUND INT16_MIN
  • 自定義隊列:
    dispatch_queue_create(queuename, attr)
    第一個參數制定隊列名稱,自定義隊列可以是串行也可以是並行的,由第二個參數attr決定。

    DISPATCH_QUEUE_SERIAL //(NULL) 串行
    DISPATCH_QUEUE_SERIAL_INACTIVE
    DISPATCH_QUEUE_CONCURRENT //並行

    多線程執行

  • 同步執行:
    dispatch_sync(dispatch_queue_t  _Nonnull queue, ^(void)block)

  • 異步執行:
    dispatch_async(dispatch_queue_t  _Nonnull queue, ^(void)block)

  • 一次性執行:

tatic dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        code to be executed once    });

  • 延時執行:

dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{        code to be executed after a specified delay    });

  • 循環迭代執行:
    dispatch_apply(size_t iterations, dispatch_queue_t  _Nonnull queue, ^(size_t)block)
    能並發地執行不同的迭代。這個函數是同步的,所以和普通的 for 循環一樣,它只會在所有工作都完成後才會返回。

  • 柵欄(barrier)執行:
    dispatch_barrier_sync(dispatch_queue_t  _Nonnull queue, ^(void)block)  dispatch_barrier_async(dispatch_queue_t  _Nonnull queue, ^(void)block)
    柵欄執行的意思是:在barrier前面的任務執行結束後它才執行,而且它後面的任務等它執行完成之後才會執行。

  • 分組執行:
    dispatch_group_async(dispatch_group_t  _Nonnull group, dispatch_queue_t  _Nonnull queue, ^(void)block)  dispatch_group_notify(dispatch_group_t  _Nonnull group, dispatch_queue_t  _Nonnull queue, ^(void)block)
    dispatch_group_async可以實現監聽一組任務是否完成,完成後得到通知執行其他的操作
    信號量

    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
    dispatch_semaphore_signal(semaphore); 
    dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, kDefaultTimeoutLengthInNanoSeconds); dispatch_semaphore_wait(semaphore, timeoutTime)

    Dispatch Source

    dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatchQueue);
      dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, intervalInSeconds * NSEC_PER_SEC, leewayInSeconds * NSEC_PER_SEC);
      dispatch_source_set_event_handler(timer, ^{
          code to be executed when timer fires
      });
      dispatch_resume(timer);

    代碼示例

    柵欄執行:

- (void)testBarrier{
    dispatch_queue_t queue = dispatch_queue_create("com.zyw.test", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async1");
    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"dispatch_async2");
    });
    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async");
        [NSThread sleepForTimeInterval:2];

    });
    dispatch_async(queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"dispatch_async3");   
    });
}

輸出:

2016-12-02 17:17:24.710 TestThread[1434:164682] dispatch_async1
2016-12-02 17:17:25.713 TestThread[1434:164681] dispatch_async2
2016-12-02 17:17:25.713 TestThread[1434:164681] dispatch_barrier_async
2016-12-02 17:17:28.720 TestThread[1434:164681] dispatch_async3

分組執行:

- (void)testGroup{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:1];
        NSLog(@"group1");
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:2];
        NSLog(@"group2");
    });
    dispatch_group_async(group, queue, ^{
        [NSThread sleepForTimeInterval:3];
        NSLog(@"group3");
    });
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"EndAllgroupTask");
    });
//    第二種使用方法
//    dispatch_group_enter(group);
//    dispatch_group_leave(group);
//    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
//        
//    });
}

輸出:

2016-12-02 17:11:50.592 TestThread[1407:161853] group1
2016-12-02 17:11:51.591 TestThread[1407:161852] group2
2016-12-02 17:11:52.589 TestThread[1407:161855] group3
2016-12-02 17:11:52.590 TestThread[1407:161635] EndAllgroupTask

信號量:

- (void)testSemaphore{
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(5);
    for (NSInteger i = 0 ; i < 10; i++) {
        dispatch_async(queue, ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
            NSLog(@"*****%zd", i);
            [NSThread sleepForTimeInterval:1];
            dispatch_semaphore_signal(semaphore);
        });
    }
}

輸出:

2016-12-02 17:36:11.071 TestThread[1496:173204] *****1
2016-12-02 17:36:11.071 TestThread[1496:173207] *****2
2016-12-02 17:36:11.071 TestThread[1496:173203] *****0
2016-12-02 17:36:11.071 TestThread[1496:173242] *****3
2016-12-02 17:36:11.071 TestThread[1496:173243] *****4

2016-12-02 17:36:12.074 TestThread[1496:173244] *****5
2016-12-02 17:36:12.074 TestThread[1496:173250] *****9
2016-12-02 17:36:12.074 TestThread[1496:173246] *****7
2016-12-02 17:36:12.074 TestThread[1496:173245] *****6
2016-12-02 17:36:12.074 TestThread[1496:173247] *****8

Dispatch Source

- (void)testSource{
    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_time_t start = dispatch_time(DISPATCH_TIME_NOW, 5);
    dispatch_source_set_timer(timer, start, 3 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
    dispatch_source_set_event_handler(timer, ^{
        NSLog(@"******Hello");
    });
    //啟動
    dispatch_resume(timer);
}

同步異步串行並行簡單常用未列出

分析說明

同步異步和並行串行

並行隊列串行隊列主隊列異步執行開啟多個新的線程,任務同時執行開啟一個新的線程,任務按順序執行不開啟新的線程,任務按順序執行同步執行不開啟新的線程,任務按順序執行不開啟新的線程,任務按順序執行死鎖

對於GCD中的block執行,都有對應的function執行函數,比如:

dispatch_sync_f(dispatch_queue_t  _Nonnull queue, void * _Nullable context, dispatch_function_t  _Nonnull work)
dispatch_async_f(dispatch_queue_t  _Nonnull queue, void * _Nullable context, dispatch_function_t  _Nonnull work)

信號量
dispatch_semaphore_create(N);N>=0時可以正常執行,當N小於0時一直等待
代碼示例的解釋:創建了一個初使值為5的semaphore,每一次for循環都會創建一個新的線程(具體看GCD的線程池,此處認為是新建),線程結束的時候會發送一個信號,線程創建之前會信號等待,所以當同時創建了5個線程之後,for循環就會阻塞,等待有線程結束之後會增加一個信號才繼續執行,如此就形成了對並發的控制,如上就是一個並發數為5的一個線程隊列。
Dispatch Source
它基本上就是一個低級函數的 grab-bag,可監聽事件如下:
DISPATCH_SOURCE_TYPE_DATA_ADD:屬於自定義事件,可以通過dispatch_source_get_data函數獲取事件變量數據,在我們自定義的方法中可以調用dispatch_source_merge_data函數向Dispatch Source設置數據,下文中會有詳細的演示。
DISPATCH_SOURCE_TYPE_DATA_OR:屬於自定義事件,用法同上面的類型一樣。
DISPATCH_SOURCE_TYPE_MACH_SEND:Mach端口發送事件。
DISPATCH_SOURCE_TYPE_MACH_RECV:Mach端口接收事件。
DISPATCH_SOURCE_TYPE_PROC:與進程相關的事件。
DISPATCH_SOURCE_TYPE_READ:讀文件事件。
DISPATCH_SOURCE_TYPE_WRITE:寫文件事件。
DISPATCH_SOURCE_TYPE_VNODE:文件屬性更改事件。
DISPATCH_SOURCE_TYPE_SIGNAL:接收信號事件。
DISPATCH_SOURCE_TYPE_TIMER:定時器事件。
DISPATCH_SOURCE_TYPE_MEMORYPRESSURE:內存壓力事件。

  1. 上一頁:
  2. 下一頁:
蘋果刷機越獄教程| IOS教程問題解答| IOS技巧綜合| IOS7技巧| IOS8教程
Copyright © Ios教程網 All Rights Reserved