iOS多線程之NSThread,GCD,NSOperation

timg.jpeg

基本概念

進程: 一個具有一定獨立功能的程序關(guān)于某個數(shù)據(jù)集合的一次運行活動??梢岳斫獬梢粋€運行中的應(yīng)用程序。
線程: 程序執(zhí)行流的最小單元,線程是進程中的一個實體。
同步: 只能在當前線程按先后順序依次執(zhí)行,不開啟新線程。
異步: 可以在當前線程開啟多個新線程執(zhí)行,可不按順序執(zhí)行。
隊列: 裝載線程任務(wù)的隊形結(jié)構(gòu)。
并發(fā): 線程執(zhí)行可以同時一起進行執(zhí)行。
串行: 線程執(zhí)行只能依次逐一先后有序的執(zhí)行。一個進程可以有多個線程,也可以有多個隊列。

iOS多線程對比

  1. NSThread
    每個NSThread對象對應(yīng)一個線程,真正最原始的線程。
    1)優(yōu)點:NSThread 輕量級最低,相對簡單。
    2)缺點:手動管理所有的線程活動,如生命周期、線程同步、睡眠等。

  2. NSOperation
    自帶線程管理的抽象類。
    1)優(yōu)點:自帶線程周期管理,操作上可更注重自己邏輯。
    2)缺點:面向?qū)ο蟮某橄箢悾荒軐崿F(xiàn)它或者使用它定義好的兩個子類:NSInvocationOperation 和 NSBlockOperation。

  3. GCD
    Grand Central Dispatch (GCD)是Apple開發(fā)的一個多核編程的解決方法。
    1)優(yōu)點:最高效,避開并發(fā)陷阱。
    2)缺點:基于C實現(xiàn)。

  4. 選擇小結(jié)
    1)簡單而安全的選擇NSOperation實現(xiàn)多線程即可。
    2)處理大量并發(fā)數(shù)據(jù),又追求性能效率的選擇GCD。
    3)NSThread用的少。

1. NSThread 創(chuàng)建方法一

- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 創(chuàng)建線程
    NSThread *thread = [[NSThread alloc]initWithTarget:self selector: @selector(run:) object:@"hehe"];
    //線程名字
    thread.name = @"wang";
    //開啟線程
    [thread start];
    
}
- (void)run:(NSString*)str
{
    //d[2177:95263] -----run-----hehe--<NSThread: 0x60000026e440>{number = 3, name = wang}
     NSLog(@"-----run-----%@--%@", str, [NSThread currentThread]);
}

2. NSThread 其他創(chuàng)建方法

//方法二
[self performSelectorInBackground:@selector(run:) withObject:@"wang"];
//方法三

 [NSThread detachNewThreadSelector:@selector(run:) toTarget:self withObject:@"wang"];

3. 線程的狀態(tài)

9F77DB76-6699-4DFE-BAFC-C9E6A8F5B534.png

4. 線程安全

//self 是鎖對象
@synchronized(self) {
            //執(zhí)行多線程操作
}

5. NSThread的其他api

// 獲得主線程
+ (NSThread *)mainThread; 

// 是否為主線程
- (BOOL)isMainThread; 

// 是否為主線程
+ (BOOL)isMainThread; 

//獲得當前線程
NSThread *current = [NSThread currentThread];

//線程的名字
- (void)setName:(NSString *)n;
- (NSString *)name;

//啟動線程,進入就緒狀態(tài) -> 運行狀態(tài)。當線程任務(wù)執(zhí)行完畢,自動進入死亡狀態(tài)
- (void)start; 

//阻塞(暫停)線程
+ (void)sleepUntilDate:(NSDate *)date;
+ (void)sleepForTimeInterval:(NSTimeInterval)ti;

//強制停止線程進入死亡狀態(tài)一旦線程停止(死亡)了,就不能再次開啟任務(wù)
+ (void)exit;

//線程間通信常用方法
- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;
- (void)performSelector:(SEL)aSelector onThread:(NSThread *)thr withObject:(id)arg waitUntilDone:(BOOL)wait;

6. GCD的任務(wù)和隊列

  • 異步函數(shù) + 并發(fā)隊列:可以同時開啟多條線程
/**
 * 異步函數(shù) + 并發(fā)隊列:可以同時開啟多條線程
 */
- (void)asyncConcurrent
{
    // 1.創(chuàng)建一個并發(fā)隊列
    // label : 相當于隊列的名字
    // attr  : 并行 DISPATCH_QUEUE_CONCURRENT 串行 DISPATCH_QUEUE_SERIAL == NULL
//    dispatch_queue_t queue = dispatch_queue_create("wang", DISPATCH_QUEUE_CONCURRENT);
    
    // 1.獲得全局的并發(fā)隊列
    //#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_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務(wù)加入隊列
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<2; i++) {
            NSLog(@"1-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<2; i++) {
            NSLog(@"2-----%@", [NSThread currentThread]);
        }
    });
    dispatch_async(queue, ^{
        for (NSInteger i = 0; i<2; i++) {
            NSLog(@"3-----%@", [NSThread currentThread]);
        }
    });
    
//    2017-03-28 17:10:40.609 07-GCD的基本使用[5361:272988] 3-----<NSThread: 0x60800026b9c0>{number = 5, name = (null)}
//    2017-03-28 17:10:40.609 07-GCD的基本使用[5361:272971] 1-----<NSThread: 0x60800026ba80>{number = 3, name = (null)}
//    2017-03-28 17:10:40.609 07-GCD的基本使用[5361:272973] 2-----<NSThread: 0x60800026be40>{number = 4, name = (null)}
//    2017-03-28 17:10:40.614 07-GCD的基本使用[5361:272988] 3-----<NSThread: 0x60800026b9c0>{number = 5, name = (null)}
//    2017-03-28 17:10:40.615 07-GCD的基本使用[5361:272971] 1-----<NSThread: 0x60800026ba80>{number = 3, name = (null)}
//    2017-03-28 17:10:40.615 07-GCD的基本使用[5361:272973] 2-----<NSThread: 0x60800026be40>{number = 4, name = (null)}
    
    
}

  • 同步函數(shù) + 并發(fā)隊列:不會開啟新的線程
/**
 * 同步函數(shù) + 并發(fā)隊列:不會開啟新的線程
 */
- (void)syncConcurrent
{
    // 1.獲得全局的并發(fā)隊列
    //#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_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    // 2.將任務(wù)加入隊列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
//    2017-03-28 17:13:31.051 07-GCD的基本使用[5403:275128] 1-----<NSThread: 0x608000263940>{number = 1, name = main}
//    2017-03-28 17:13:31.053 07-GCD的基本使用[5403:275128] 2-----<NSThread: 0x608000263940>{number = 1, name = main}
//    2017-03-28 17:13:31.054 07-GCD的基本使用[5403:275128] 3-----<NSThread: 0x608000263940>{number = 1, name = main}
    
    
}


  • 異步函數(shù) + 串行隊列:會開啟新的線程,但是任務(wù)是串行的,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)
/**
 * 異步函數(shù) + 串行隊列:會開啟新的線程,但是任務(wù)是串行的,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)
 */
- (void)asyncSerial
{
    // 1.創(chuàng)建串行隊列
    //#define DISPATCH_QUEUE_SERIAL NULL
    dispatch_queue_t queue = dispatch_queue_create("wang", NULL);

    
    // 2.將任務(wù)加入隊列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
//    2017-03-28 17:16:02.187 07-GCD的基本使用[5495:278209] 1-----<NSThread: 0x608000262440>{number = 3, name = (null)}
//    2017-03-28 17:16:02.187 07-GCD的基本使用[5495:278209] 2-----<NSThread: 0x608000262440>{number = 3, name = (null)}
//    2017-03-28 17:16:02.188 07-GCD的基本使用[5495:278209] 3-----<NSThread: 0x608000262440>{number = 3, name = (null)}
}

  • 同步函數(shù) + 串行隊列:不會開啟新的線程,在當前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)
/**
 * 同步函數(shù) + 串行隊列:不會開啟新的線程,在當前線程執(zhí)行任務(wù)。任務(wù)是串行的,執(zhí)行完一個任務(wù),再執(zhí)行下一個任務(wù)
 */
- (void)syncSerial
{
    // 1.創(chuàng)建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("wang", DISPATCH_QUEUE_SERIAL);
    
    // 2.將任務(wù)加入隊列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
//    2017-03-28 17:17:25.275 07-GCD的基本使用[5527:279554] 1-----<NSThread: 0x6000000757c0>{number = 1, name = main}
//    2017-03-28 17:17:25.275 07-GCD的基本使用[5527:279554] 2-----<NSThread: 0x6000000757c0>{number = 1, name = main}
//    2017-03-28 17:17:25.275 07-GCD的基本使用[5527:279554] 3-----<NSThread: 0x6000000757c0>{number = 1, name = main}
    
}

  • 異步函數(shù) + 主隊列:只在主線程中執(zhí)行任務(wù)
/**
 * 異步函數(shù) + 主隊列:只在主線程中執(zhí)行任務(wù)
 */
- (void)asyncMain
{
    // 1.獲得主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.將任務(wù)加入隊列
    dispatch_async(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
//    2017-03-28 17:20:38.682 07-GCD的基本使用[5591:282861] 1-----<NSThread: 0x600000065a40>{number = 1, name = main}
//    2017-03-28 17:20:38.683 07-GCD的基本使用[5591:282861] 2-----<NSThread: 0x600000065a40>{number = 1, name = main}
//    2017-03-28 17:20:38.683 07-GCD的基本使用[5591:282861] 3-----<NSThread: 0x600000065a40>{number = 1, name = main}
}
  • 同步函數(shù) + 主隊列:堵塞主線程,禁用
/**
 * 同步函數(shù) + 主隊列:
 */
- (void)syncMain
{
    NSLog(@"syncMain ----- begin");
    
    // 1.獲得主隊列
    dispatch_queue_t queue = dispatch_get_main_queue();
    
    // 2.將任務(wù)加入隊列
    dispatch_sync(queue, ^{
        NSLog(@"1-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"2-----%@", [NSThread currentThread]);
    });
    dispatch_sync(queue, ^{
        NSLog(@"3-----%@", [NSThread currentThread]);
    });
    
    NSLog(@"syncMain ----- end");
}

7. GCD線程間的通信

 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
          //執(zhí)行異步操作
        
        // 回到主線程
        dispatch_async(dispatch_get_main_queue(), ^{
            //刷新UI
        });
    });

8. GCD延時執(zhí)行

/**
 * 延遲執(zhí)行
 */
- (void)delay
{
    
    //方法1
    [self performSelector:@selector(run) withObject:nil afterDelay:2.0];
    
    //方法2
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        
        NSLog(@"延遲執(zhí)行");
    });
    
    //方法3
    [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(run) userInfo:nil repeats:NO];
    
}

8. GCD只執(zhí)行1次的代碼dispatch_once

- (void)once
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSLog(@"只執(zhí)行1次的代碼dispatch_once");
    });
    
}

9. GCD的dispatch_barrier柵欄

- (void)barrier
{
    dispatch_queue_t queue = dispatch_queue_create("12312312", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"----1-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----2-----%@", [NSThread currentThread]);
    });

    
    dispatch_barrier_async(queue, ^{
        NSLog(@"----barrier-----%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        NSLog(@"----3-----%@", [NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"----4-----%@", [NSThread currentThread]);
    });
 
//    2017-03-29 10:20:17.716 [1321:43448] ----1-----<NSThread: 0x608000264740>{number = 3, name = (null)}
//    2017-03-29 10:20:17.716 [1321:43722] ----2-----<NSThread: 0x60800007f7c0>{number = 4, name = (null)}
//    2017-03-29 10:20:17.716 [1321:43722] ----barrier-----<NSThread: 0x60800007f7c0>{number = 4, name = (null)}
//    2017-03-29 10:20:17.717 [1321:43722] ----3-----<NSThread: 0x60800007f7c0>{number = 4, name = (null)}
//    2017-03-29 10:20:17.717 [1321:43448] ----4-----<NSThread: 0x608000264740>{number = 3, name = (null)}

}

10. GCD的dispatch_apply

- (void)apply
{
  
    
    NSMutableArray *arr = [NSMutableArray array];
    
    for (int i=0; i<10; i++) {
        
        [arr addObject:@(i)];
    }
    
     dispatch_queue_t queue1 = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(arr.count, queue1, ^(size_t index) {
        
        NSLog(@"%ld",index);
        
    });
    
//    2017-03-29 10:44:58.489 [1877:73243] 2
//    2017-03-29 10:44:58.489 [1877:73244] 1
//    2017-03-29 10:44:58.489 [1877:73213] 0
//    2017-03-29 10:44:58.489 [1877:73246] 3
//    2017-03-29 10:44:58.489 [1877:73243] 4
//    2017-03-29 10:44:58.489 [1877:73244] 5
//    2017-03-29 10:44:58.489 [1877:73213] 6
//    2017-03-29 10:44:58.489 [1877:73246] 7
//    2017-03-29 10:44:58.489 [1877:73243] 8
//    2017-03-29 10:44:58.490 [1877:73244] 9
    
}

11. GCD的dispatch_group

- (void)group
{
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    // 創(chuàng)建一個隊列組
    dispatch_group_t group = dispatch_group_create();
    
    
    dispatch_group_async(group, queue, ^{
      
        NSLog(@"1");
        
    });
    
    dispatch_group_async(group, queue, ^{
        NSLog(@"2");
    });
    
    dispatch_group_notify(group, queue, ^{
        NSLog(@"完成了");
    });
    
//    2017-03-29 10:48:53.947 [1939:76156] 2
//    2017-03-29 10:48:53.947 [1939:76157] 1
//    2017-03-29 10:48:53.947 [1939:76157] 完成了
}

11. NSOperation的使用

- (void)blockOperation
{
    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
        // 在主線程
        NSLog(@"下載1------%@", [NSThread currentThread]);
    }];
    
    // 添加額外的任務(wù)(在子線程執(zhí)行)
    [op addExecutionBlock:^{
        NSLog(@"下載2------%@", [NSThread currentThread]);
    }];

    [op addExecutionBlock:^{
        NSLog(@"下載3------%@", [NSThread currentThread]);
    }];
    [op addExecutionBlock:^{
        NSLog(@"下載4------%@", [NSThread currentThread]);
    }];
    
    [op start];
}

- (void)invocationOperation
{
    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(run) object:nil];

    [op start];
}

- (void)run
{
    NSLog(@"------%@", [NSThread currentThread]);
}

12. NSOperationQueue的使用

NSOperationQueue *queue = [[NSOperationQueue alloc] init];是可以開啟線程的,串行和并行通過maxConcurrentOperationCount設(shè)置,為1的時候就是串行。[NSOperationQueue mainQueue]是在主線程執(zhí)行任務(wù)。串行的

- (void)operationQueue1
{
    // 創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 創(chuàng)建操作(任務(wù))
    // 創(chuàng)建NSInvocationOperation
    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(download1) object:nil];
    
    // 創(chuàng)建NSBlockOperation
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3 --- %@", [NSThread currentThread]);
    }];
    
    [op3 addExecutionBlock:^{
        NSLog(@"download4 --- %@", [NSThread currentThread]);
    }];
    
    // 添加任務(wù)到隊列中
    [queue addOperation:op1]; // [op1 start]
    [queue addOperation:op3]; // [op3 start]

}

- (void)operationQueue2
{
    // 創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
  
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
    }];
}

- (void)opetationQueue3
{
    // 創(chuàng)建隊列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    // 設(shè)置最大并發(fā)操作數(shù)
    //    queue.maxConcurrentOperationCount = 2;
    queue.maxConcurrentOperationCount = 1; // 就變成了串行隊列
    
    // 添加操作
    [queue addOperationWithBlock:^{
        NSLog(@"download1 --- %@", [NSThread currentThread]);
        
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"download2 --- %@", [NSThread currentThread]);
        
    }];
   
}

- (void)suspend
{
    if (self.queue.isSuspended) {
        // 恢復(fù)隊列,繼續(xù)執(zhí)行
        self.queue.suspended = NO;
    } else {
        // 暫停(掛起)隊列,暫停執(zhí)行
        self.queue.suspended = YES;
    }
    
    
    //取消任務(wù)
    [self.queue cancelAllOperations];
}

12. 自定義NSOperation的使用

@interface MyOperation : NSOperation

@end

@implementation MyOperation

/**
 * 需要執(zhí)行的任務(wù)
 */
- (void)main
{
    NSLog(@"需要執(zhí)行的任務(wù)");
}

@end

13. 任務(wù)之間添加依賴addDependency

- (void) addDependency{
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download1----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download2----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download3----%@", [NSThread  currentThread]);
    }];
    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
        for (NSInteger i = 0; i<10; i++) {
            NSLog(@"download4----%@", [NSThread  currentThread]);
        }
    }];
    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"download5----%@", [NSThread  currentThread]);
    }];
    op5.completionBlock = ^{
        NSLog(@"op5執(zhí)行完畢---%@", [NSThread currentThread]);
    };
    
    // 設(shè)置依賴
    [op3 addDependency:op1];
    [op3 addDependency:op2];
    [op3 addDependency:op4];
    
    [queue addOperation:op1];
    [queue addOperation:op2];
    [queue addOperation:op3];
    [queue addOperation:op4];
    [queue addOperation:op5];
}

13. 線程之間的通信

/**
 * 線程之間的通信
 */
- (void)test
{
    [[[NSOperationQueue alloc] init] addOperationWithBlock:^{
        // 圖片的網(wǎng)絡(luò)路徑
       NSURL *url = [NSURL URLWithString:@"http://img.pconline.com.cn/images/photoblog/9/9/8/1/9981681/200910/11/1255259355826.jpg"];
     
        
        // 加載圖片
        NSData *data = [NSData dataWithContentsOfURL:url];
        
        // 生成圖片
        UIImage *image = [UIImage imageWithData:data];
        
        // 回到主線程
        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
            self.imageView.image = image;
        }];
    }];
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容