iOS-多線程(NSThread+GCD+NSOperation)

一、基本概念

  1. 進(jìn)程:系統(tǒng)中正在運(yùn)行的應(yīng)用程序。每個(gè)程序都是相互獨(dú)立的,并且運(yùn)行在一塊獨(dú)有的受保護(hù)的內(nèi)存中
  2. 線程:進(jìn)程基本執(zhí)行路徑。一個(gè)進(jìn)程想要執(zhí)行任務(wù),就必須通過線程來執(zhí)行。單條的線程通過串行的方式執(zhí)行任務(wù),也就是說任務(wù)是一個(gè)一個(gè)按順序的執(zhí)行
  3. 多線程:開啟多條線程,讓任務(wù)并發(fā)執(zhí)行。(原理:在同一時(shí)間內(nèi),CPU 只會(huì)處理一條線程,多線程并發(fā)執(zhí)行其實(shí)是,cpu在不同的線程之間快速的切換處理,只要cpu切換的足夠快,就會(huì)給人多線程并發(fā)執(zhí)行任務(wù)的假象)
    補(bǔ)充:并行指的是任務(wù)一起執(zhí)行,強(qiáng)調(diào)的是過程;并發(fā)指的是任務(wù)以一起執(zhí)行的方式開始執(zhí)行,強(qiáng)調(diào)的是任務(wù)開始執(zhí)行的方式。

二、多線程的優(yōu)缺點(diǎn)

優(yōu)點(diǎn)
  1. 可提高程序的執(zhí)行效率
  2. 可合理提高cpu和內(nèi)存的利用率
缺點(diǎn)
  1. 開啟一條線程所占用的內(nèi)存較大(主線程 1M ;子線程 512KB)
  2. 線程多時(shí),cpu在在切換線程上的開銷較大,影響性能
  3. 線程間的通訊和數(shù)據(jù)共享等會(huì)將大程序設(shè)計(jì)的復(fù)雜度

三、多線程的實(shí)現(xiàn)方案

1. NSThread

此方案是經(jīng)過蘋果封裝后完全面向?qū)ο蟮模梢灾苯幽玫骄€程對(duì)象進(jìn)行操作,很直觀、便捷。但是缺點(diǎn)就是需手動(dòng)管理線程生命周期,所以不經(jīng)常使用
1.1 創(chuàng)建(3種方式)

//  1.手動(dòng)啟動(dòng)線程,可拿到線程對(duì)象進(jìn)行額外的設(shè)置
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(addThread:) object:@"hahaha"];
    [thread start];
    
    //  2.分離出子線程 - 自動(dòng)啟動(dòng)線程
    [NSThread detachNewThreadSelector:@selector(addChildThread:) toTarget:self withObject:@"子線程"];
    
    //  3.后臺(tái)線程 - 自動(dòng)啟動(dòng)線程
    [self performSelectorInBackground:@selector(addBackgroundThread:) withObject:@"后臺(tái)線程"];

1.2 NSThread 常涉及到的方法

    [thread cancel];  //取消線程
    [NSThread exit];
    [NSThread sleepForTimeInterval:3.0];  //阻塞線程
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:3.0]];
    [NSThread currentThread];  //獲取當(dāng)前線程
    [NSThread mainThread];  //獲取主線程
    [NSThread isMultiThreaded];  //是否是多線程,返回BOOL值

1.3 線程間通信

- (void)viewDidLoad {
    [super viewDidLoad];

    //  創(chuàng)建子線程
    NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(addThread:) object:@"hahaha"];
    [thread start];
}

- (void)addThread:(NSString *)name {
    NSLog(@"%@",[NSThread currentThread]);
    
    //  在子線程中回到主線程的兩種方式
    [self performSelectorOnMainThread:@selector(goBackMainThread:) withObject:@"main" waitUntilDone:YES];
    [self performSelector:@selector(goBackMainThread:) onThread:[NSThread mainThread] withObject:@"test" waitUntilDone:YES];
}

- (void)goBackMainThread:(NSString *)name {
    NSLog(@"%@",[NSThread currentThread]);
}

打印結(jié)果

2016-08-01 14:20:30.253 demo[66988:3541822] <NSThread: 0x79e71ab0>{number = 2, name = (null)}
2016-08-01 14:20:30.258 demo[66988:3541733] <NSThread: 0x79e72a30>{number = 1, name = main}
2016-08-01 14:20:30.259 demo[66988:3541733] <NSThread: 0x79e72a30>{number = 1, name = main}

1.4 線程安全
當(dāng)多個(gè)線程訪問同一塊資源時(shí)會(huì)發(fā)生數(shù)據(jù)安全問題,也稱為線程同步 ,這時(shí)候就需要加互斥鎖 @synchronized(self){}

2. GCD

蘋果為多核并行運(yùn)算開發(fā)的解決方案,它可以自動(dòng)的利用cpu的內(nèi)核,并且系統(tǒng)自動(dòng)管理線程的生命周期
開發(fā)者要做的是將任務(wù)添加到隊(duì)列中,系統(tǒng)會(huì)自動(dòng)將隊(duì)列中的任務(wù)按FIFO的原則取出任務(wù)(FIFO:先進(jìn)先出,后進(jìn)后出,如果以并發(fā)方式執(zhí)行任務(wù),則先出的任務(wù)還沒結(jié)束前下一個(gè)的任務(wù)可以出列)

2.1 任務(wù)和隊(duì)列的理解
任務(wù):決定能不能開啟新線程,分同步和異步任務(wù)兩種

-同步:任務(wù)只能在當(dāng)前的線程執(zhí)行,不具備開啟新線程的能力(同步任務(wù)比較霸道,必須在當(dāng)前的分配任務(wù)執(zhí)行完,才算把分配任務(wù)的任務(wù)結(jié)束,和后面說的死鎖有關(guān)聯(lián))
-異步:任務(wù)可以在其它線程中執(zhí)行,具備有開啟新線程的能力

    //  同步任務(wù)
    dispatch_sync(dispatch_get_global_queue(0, 0), ^{
    });
    //  異步任務(wù)
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
    });
隊(duì)列:決定任務(wù)將以什么方式執(zhí)行,分串行和并發(fā)隊(duì)列兩種

-串行:任務(wù)將一個(gè)接一個(gè)的執(zhí)行(一個(gè)任務(wù)執(zhí)行完才開始下一個(gè)任務(wù))
-并發(fā):多個(gè)任務(wù)可以并發(fā)(同時(shí))執(zhí)行,要在異步任務(wù)函數(shù)中才能有效

隊(duì)列的創(chuàng)建

-串行:系統(tǒng)提供兩種方式

    //  1.系統(tǒng)提供的主隊(duì)列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    //  2.創(chuàng)建新的串行隊(duì)列(參數(shù)1:隊(duì)列名字,apple推薦使用com.example.myQueue的規(guī)則來命名隊(duì)列,用于debug的時(shí)候追蹤隊(duì)列以便于調(diào)試,可以為空;
    //    參數(shù)2:可以傳人NULL或DISPATCH_QUEUE_SERIAL表示串行隊(duì)列)
    dispatch_queue_t queue = dispatch_queue_create("com.hhh.eatShit", NULL);

- 并發(fā):系統(tǒng)提供兩種方式

    //  1.系統(tǒng)提供的全局隊(duì)列(參數(shù)1:隊(duì)列的優(yōu)先級(jí);參數(shù)2:傳0即可)
    dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //  2.創(chuàng)建新的并發(fā)隊(duì)列
    dispatch_queue_t queue = dispatch_queue_create("com.hhh.eatShit", DISPATCH_QUEUE_CONCURRENT);
2.2 GCD使用方式(任務(wù)和隊(duì)列的結(jié)合方式)
  1. 異步任務(wù)+并發(fā)隊(duì)列:使用頻率高,開啟多線程并發(fā)的執(zhí)行任務(wù)
  2. 異步任務(wù)+串行隊(duì)列:使用頻率高,開啟一條新線程,串行執(zhí)行任務(wù)
  3. 同步任務(wù)+并發(fā)隊(duì)列:基本不用,不開線程在當(dāng)前線程串行執(zhí)行任務(wù)我
  4. 同步任務(wù)+串行隊(duì)列:基本不用,不開線程在當(dāng)前線程串行執(zhí)行任務(wù)我
  5. 異步任務(wù)+主隊(duì)列:不開線程,在主線程中串行執(zhí)行任務(wù)我
  6. 同步任務(wù)+主隊(duì)列:不開線程,在主線程中串行執(zhí)行任務(wù)我(注意死鎖)
    注意:在當(dāng)前的串行隊(duì)列中添加同步任務(wù)到當(dāng)前隊(duì)列會(huì)發(fā)生死鎖
- (void)viewDidLoad {
    [super viewDidLoad];
    
    //  主隊(duì)列是串行的,在主隊(duì)里中往主隊(duì)列添加同步任務(wù),發(fā)生死鎖,無(wú)法執(zhí)行打印內(nèi)容
    dispatch_sync(dispatch_get_main_queue(), ^{
        NSLog(@"快把我打印出來");
    });
}
2.3 ??演示GCD的使用
  1. 異步任務(wù)+并發(fā)隊(duì)列
- (void)viewDidLoad {
    [super viewDidLoad];

    //  由系統(tǒng)決定開幾條線程
    dispatch_queue_t queue = dispatch_queue_create("com.demo.bingfa", DISPATCH_QUEUE_CONCURRENT);
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    });
}

打印

2016-08-01 17:27:01.793 demo[79533:3667100] 任務(wù)1---<NSThread: 0x79858c40>{number = 2, name = (null)}
2016-08-01 17:27:01.793 demo[79533:3667104] 任務(wù)3---<NSThread: 0x7865da50>{number = 4, name = (null)}
2016-08-01 17:27:01.794 demo[79533:3667103] 任務(wù)2---<NSThread: 0x79a5a540>{number = 3, name = (null)}

2.異步任務(wù)+串行隊(duì)列

- (void)viewDidLoad {
    [super viewDidLoad];
    
    //  只開一條新線程
    dispatch_queue_t queue = dispatch_queue_create("com.demo.chuanxing", NULL);
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    });
}

打印

2016-08-01 17:29:13.092 demo[79710:3669166] 任務(wù)1---<NSThread: 0x7a236720>{number = 2, name = (null)}
2016-08-01 17:29:13.093 demo[79710:3669166] 任務(wù)2---<NSThread: 0x7a236720>{number = 2, name = (null)}
2016-08-01 17:29:13.094 demo[79710:3669166] 任務(wù)3---<NSThread: 0x7a236720>{number = 2, name = (null)}

3.同步任務(wù)+并發(fā)隊(duì)列

- (void)viewDidLoad {
    [super viewDidLoad];

    //  不開新線程,當(dāng)前是主線程,所以任務(wù)1、2、3在主線程中執(zhí)行
    dispatch_queue_t queue = dispatch_get_main_queue();
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    });
}

打印

2016-08-01 17:57:04.133 demo[81692:3691446] 任務(wù)1---<NSThread: 0x7a2314b0>{number = 1, name = main}
2016-08-01 17:57:04.134 demo[81692:3691446] 任務(wù)2---<NSThread: 0x7a2314b0>{number = 1, name = main}
2016-08-01 17:57:04.134 demo[81692:3691446] 任務(wù)3---<NSThread: 0x7a2314b0>{number = 1, name = main}

4.同步任務(wù)+串行隊(duì)列

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.chuanxing", NULL);
    dispatch_async(queue, ^{
        NSLog(@"新開的子線程---%@",[NSThread currentThread]);

        //  在新開的子線程中,在queue隊(duì)列中測(cè)試
        dispatch_queue_t queue1 = dispatch_queue_create("com.demo.chuanxing", NULL);
        dispatch_sync(queue1, ^{
            NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
        });
    });
}

打印

2016-08-01 17:53:10.405 demo[81376:3688029] 新開的子線程---<NSThread: 0x78e7fe80>{number = 2, name = (null)}
2016-08-01 17:53:10.406 demo[81376:3688029] 任務(wù)1---<NSThread: 0x78e7fe80>{number = 2, name = (null)}
2016-08-01 17:53:10.407 demo[81376:3688029] 任務(wù)2---<NSThread: 0x78e7fe80>{number = 2, name = (null)}
2016-08-01 17:53:10.407 demo[81376:3688029] 任務(wù)3---<NSThread: 0x78e7fe80>{number = 2, name = (null)}

5.異步任務(wù)+主隊(duì)列(同3)
6.同步任務(wù)+主隊(duì)列(切記不能在主隊(duì)列中使用這種方式)

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_queue_t queue = dispatch_queue_create("com.demo.chuanxing", NULL);
    dispatch_async(queue, ^{
        NSLog(@"新開的子線程---%@",[NSThread currentThread]);

        //  在新開的子線程中,任務(wù)1、2、3在主線程中執(zhí)行
        dispatch_queue_t queue1 = dispatch_get_main_queue();
        dispatch_sync(queue1, ^{
            NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
        });
        dispatch_sync(queue1, ^{
            NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
        });
    });
}

打印

2016-08-01 17:59:57.794 demo[81900:3693593] 新開的子線程---<NSThread: 0x79745ab0>{number = 2, name = (null)}
2016-08-01 17:59:57.798 demo[81900:3693419] 任務(wù)1---<NSThread: 0x79843a10>{number = 1, name = main}
2016-08-01 17:59:57.799 demo[81900:3693419] 任務(wù)2---<NSThread: 0x79843a10>{number = 1, name = main}
2016-08-01 17:59:57.800 demo[81900:3693419] 任務(wù)3---<NSThread: 0x79843a10>{number = 1, name = main}
2.4 GCD其它函數(shù)
  1. 柵欄函數(shù) dispatch_barrier_async(queue, ^{ });
    在任務(wù)前面的異步任務(wù)都執(zhí)行完后才會(huì)執(zhí)行它,并且等它執(zhí)行完后,后面的異步任務(wù)才能開始執(zhí)行
    注意:
    1.>此函數(shù)加入全局隊(duì)列示無(wú)效的,即queue不能是全劇隊(duì)列
    2.>如果任務(wù)是同步的則會(huì)在當(dāng)前的線程中執(zhí)行,異步的話在新線程中執(zhí)行

??

- (void)viewDidLoad {
    [super viewDidLoad];

    dispatch_queue_t queue = dispatch_queue_create("com.demo.bingfa", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"任務(wù)4---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)5---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)6---%@",[NSThread currentThread]);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)7---%@",[NSThread currentThread]);
    });
}

打印(任務(wù)4總是在任務(wù)1、2、3和5、6、7的中間)

2016-08-01 20:55:44.075 demo[85715:3735784] 任務(wù)2---<NSThread: 0x7a8746e0>{number = 3, name = (null)}
2016-08-01 20:55:44.075 demo[85715:3735778] 任務(wù)1---<NSThread: 0x7a96ff10>{number = 2, name = (null)}
2016-08-01 20:55:44.075 demo[85715:3735777] 任務(wù)3---<NSThread: 0x7a67a470>{number = 4, name = (null)}
2016-08-01 20:55:44.077 demo[85715:3735777] 任務(wù)4---<NSThread: 0x7a67a470>{number = 4, name = (null)}
2016-08-01 20:55:44.078 demo[85715:3735777] 任務(wù)5---<NSThread: 0x7a67a470>{number = 4, name = (null)}
2016-08-01 20:55:44.078 demo[85715:3735778] 任務(wù)6---<NSThread: 0x7a96ff10>{number = 2, name = (null)}
2016-08-01 20:55:44.078 demo[85715:3735784] 任務(wù)7---<NSThread: 0x7a8746e0>{number = 3, name = (null)}

信號(hào)量的使用 --- 但以上任務(wù)2中存在其它異步并發(fā)任務(wù),如網(wǎng)絡(luò)請(qǐng)求的時(shí):

   dispatch_queue_t queue = dispatch_queue_create("com.demo.bingfa", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
       
        // 創(chuàng)建一個(gè)信號(hào)量為0的信號(hào)(紅燈)
        dispatch_semaphore_t sema = dispatch_semaphore_create(0);

        dispatch_queue_t queue2 = dispatch_queue_create("com.demo.bing", DISPATCH_QUEUE_CONCURRENT);

        dispatch_async(queue2, ^{
            [NSThread sleepForTimeInterval:2];
            // 使信號(hào)的信號(hào)量+1,這里的信號(hào)量本來為0,+1信號(hào)量為1(綠燈)
            dispatch_semaphore_signal(sema);
            NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
        });
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
        // 開啟信號(hào)等待,設(shè)置等待時(shí)間為永久,直到信號(hào)的信號(hào)量大于等于1(綠燈)
        dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
    });
    dispatch_async(queue, ^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        NSLog(@"任務(wù)4---%@",[NSThread currentThread]);
    });

2.隊(duì)列組
能實(shí)現(xiàn)在一個(gè)或則多個(gè)任務(wù)執(zhí)行完之后,在執(zhí)行特定的任務(wù)。例如:A任務(wù)和B任務(wù)并發(fā)執(zhí)行,C任務(wù)需要在A任務(wù)和B任務(wù)都執(zhí)行完之后再執(zhí)行,這時(shí)候就可以通過隊(duì)列組來實(shí)現(xiàn)
??

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)4---%@",[NSThread currentThread]);
    });
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)5---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)6---%@",[NSThread currentThread]);
    });
    dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        NSLog(@"任務(wù)7---%@",[NSThread currentThread]);
    });
}

打印: 你會(huì)發(fā)現(xiàn)任務(wù)5永遠(yuǎn)都是隊(duì)列組中最后執(zhí)行的一個(gè)

2016-08-01 22:10:55.218 demo[90162:3774195] 任務(wù)1---<NSThread: 0x7af959d0>{number = 2, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774194] 任務(wù)2---<NSThread: 0x7b1346f0>{number = 3, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774216] 任務(wù)6---<NSThread: 0x7b06cf00>{number = 6, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774215] 任務(wù)4---<NSThread: 0x7af95fe0>{number = 5, name = (null)}
2016-08-01 22:10:55.218 demo[90162:3774199] 任務(wù)3---<NSThread: 0x7ae30250>{number = 4, name = (null)}
2016-08-01 22:10:55.219 demo[90162:3774217] 任務(wù)7---<NSThread: 0x7b135bb0>{number = 7, name = (null)}
2016-08-01 22:10:55.220 demo[90162:3774217] 任務(wù)5---<NSThread: 0x7b135bb0>{number = 7, name = (null)}
3.NSOperation
3.1 概念

NSOperation是對(duì)GCD的封裝;本身是一個(gè)抽象類,只能使用它的三個(gè)子類;和NSOperationQueue結(jié)合使用實(shí)現(xiàn)多線程并發(fā)

3.2 NSOperation和NSOperationQueue的理解
NSOperation三個(gè)子類

-NSInvocationOperation

- (void)viewDidLoad {
   [super viewDidLoad];
   //  不加入隊(duì)列執(zhí)行開始操作
   NSInvocationOperation *operation = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(addOperation:) object:@"NEW"];
   [operation start];
}
- (void)addOperation:(NSString *)name {
   //  在主線程中執(zhí)行,沒有意義
   NSLog(@"%@",[NSThread currentThread]);
}

-NSBlockOperation
創(chuàng)建操作時(shí)攜帶的任務(wù)在主線程,操作后面額外添加的任務(wù),系統(tǒng)自動(dòng)開啟線程執(zhí)行

- (void)viewDidLoad {
    [super viewDidLoad];
    //  不加入隊(duì)列執(zhí)行開始操作
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        //  在主線程中執(zhí)行
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        //  系統(tǒng)根據(jù)需要自動(dòng)開啟線程
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        //  系統(tǒng)根據(jù)需要自動(dòng)開啟線程
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    }];
    [operation addExecutionBlock:^{
        //  系統(tǒng)根據(jù)需要自動(dòng)開啟線程
        NSLog(@"任務(wù)4---%@",[NSThread currentThread]);
    }];
    [operation start];
}

-自定義NSOperation
一般操作過于復(fù)雜時(shí)可以使用自定義NSOperation,具有隱蔽性和可重復(fù)利用的好處

#import "JYOperation.h"
@implementation JYOperation
- (void)main {
    NSLog(@"%@",[NSThread currentThread]);
}
@end
- (void)viewDidLoad {
    [super viewDidLoad];
    //  不添加到隊(duì)列的話,任務(wù)在主線程執(zhí)行,沒有意義
    JYOperation *operation = [[JYOperation alloc] init];
    [operation start];
}
NSOperationQueue

- 主隊(duì)列
添加到主隊(duì)列中的任務(wù)都在主線程中執(zhí)行
-非主隊(duì)列
任務(wù)在子線程中執(zhí)行,控制任務(wù)以串行或并發(fā)的方式執(zhí)行,具體通過設(shè)置隊(duì)列的maxConcurrentOperationCount(最大并發(fā)數(shù))屬性來控制

注意:

1.maxConcurrentOperationCount必須在操作添加到隊(duì)列之前設(shè)置才有效
2.maxConcurrentOperationCount 默認(rèn)為-1,即默認(rèn)任務(wù)是并發(fā)執(zhí)行;等于0時(shí),不執(zhí)行任務(wù),無(wú)意義;等于1時(shí),串行執(zhí)行任務(wù);等于n時(shí),并發(fā)數(shù)為n,允許n個(gè)任務(wù)是同時(shí)執(zhí)行的

- (void)viewDidLoad {
    
    //  2.非主隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    queue.maxConcurrentOperationCount = 3;
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)3---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)4---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)5---%@",[NSThread currentThread]);
    }];
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)6---%@",[NSThread currentThread]);
    }];
}

打印

2016-08-02 10:10:56.874 demo[99820:3887770] 任務(wù)1---<NSThread: 0x7a98b340>{number = 2, name = (null)}
2016-08-02 10:10:56.874 demo[99820:3887814] 任務(wù)3---<NSThread: 0x7a71c690>{number = 4, name = (null)}
2016-08-02 10:10:56.874 demo[99820:3887769] 任務(wù)2---<NSThread: 0x7a71c450>{number = 3, name = (null)}
2016-08-02 10:10:56.875 demo[99820:3887814] 任務(wù)5---<NSThread: 0x7a71c690>{number = 4, name = (null)}
2016-08-02 10:10:56.876 demo[99820:3887810] 任務(wù)6---<NSThread: 0x7a71c840>{number = 5, name = (null)}
2016-08-02 10:10:56.876 demo[99820:3887770] 任務(wù)4---<NSThread: 0x7a98b340>{number = 2, name = (null)}
3.3 操作和隊(duì)列結(jié)合(兩種方式)
- (void)viewDidLoad {
    //  方式1:將操作加入隊(duì)列
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"任務(wù)1---%@",[NSThread currentThread]);
    }];
    [queue addOperation:operation];
    
    //  方式2:通過隊(duì)列直接加操作
    [queue addOperationWithBlock:^{
        NSLog(@"任務(wù)2---%@",[NSThread currentThread]);
    }];
}

補(bǔ)充(暫停、恢復(fù)和取消)

- (void)viewDidLoad {
    
    self.queue = [[NSOperationQueue alloc] init];
    //  任務(wù)暫停后可以恢復(fù)執(zhí)行
    if (self.queue.isSuspended) {
        self.queue.suspended = NO;
    }
    //  任務(wù)取消后不可在恢復(fù)執(zhí)行
    [self.queue cancelAllOperations];
}
3.4 設(shè)置操作依賴

通過調(diào)用操作[operation addDependency:]方法,可實(shí)現(xiàn)不同操作按指定的順序執(zhí)行

- (void)viewDidLoad {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作1---");
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作2---");
    }];
    //  operation1依賴operation2,operation2執(zhí)行完才能執(zhí)行operation1
    [operation1 addDependency:operation2];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
}

打印

2016-08-02 11:02:19.161 demo[3839:3930104] 操作2---
2016-08-02 11:02:19.162 demo[3839:3930104] 操作1---

??有一種需求:前兩個(gè)異步任務(wù)都執(zhí)行完之后才開始執(zhí)行第三個(gè)
任務(wù)

- (void)viewDidLoad {
    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    
    NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作1---");
    }];
    NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作2---");
    }];
    NSBlockOperation *operation3 = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"操作3---");
    }];

    [operation3 addDependency:operation1];
    [operation3 addDependency:operation2];
    [queue addOperation:operation1];
    [queue addOperation:operation2];
    [queue addOperation:operation3];
}

打印

2016-08-02 11:05:21.480 demo[4099:3932866] 操作2---
2016-08-02 11:05:21.480 demo[4099:3932865] 操作1---
2016-08-02 11:05:21.482 demo[4099:3932865] 操作3---
4 總結(jié)

以上主要涉及到開發(fā)中常用的一些內(nèi)容,另外至于GCD和NSOperation線程間的通信,其實(shí)常用到的是異步操作后回到主線程,只要將需要回到主線程的任務(wù)添加到主隊(duì)列即可

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,237評(píng)論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,957評(píng)論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,248評(píng)論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,356評(píng)論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,081評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,485評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,534評(píng)論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,720評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,263評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,025評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,204評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,787評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,461評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,874評(píng)論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,105評(píng)論 1 289
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,945評(píng)論 3 395
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,205評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容