iOS中的NSOperation和NSOperationQueue

//NSInvocationOperation  NSBlockOperation
    /**
     操作隊(duì)列(Operation Queues):
     這里的隊(duì)列指操作隊(duì)列弓颈,即用來存放操作的隊(duì)列忽肛。不同于 GCD 中的調(diào)度隊(duì)列 FIFO(先進(jìn)先出)的原則。NSOperationQueue 對于添加到隊(duì)列中的操作营勤,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴關(guān)系),然后進(jìn)入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對的優(yōu)先級決定(優(yōu)先級是操作對象自身的屬性)淹父。
     操作隊(duì)列通過設(shè)置最大并發(fā)操作數(shù)(maxConcurrentOperationCount)來控制并發(fā)世蔗、串行。
     NSOperationQueue 為我們提供了兩種不同類型的隊(duì)列:主隊(duì)列和自定義隊(duì)列颜矿。主隊(duì)列運(yùn)行在主線程之上,而自定義隊(duì)列在后臺執(zhí)行嫉晶。
     */
    
    /**
     NSOperation 實(shí)現(xiàn)多線程的使用步驟分為三步:

     創(chuàng)建操作:先將需要執(zhí)行的操作封裝到一個(gè) NSOperation 對象中骑疆。
     創(chuàng)建隊(duì)列:創(chuàng)建 NSOperationQueue 對象。
     將操作加入到隊(duì)列中:將 NSOperation 對象添加到 NSOperationQueue 對象中替废。
     之后呢箍铭,系統(tǒng)就會(huì)自動(dòng)將 NSOperationQueue 中的 NSOperation 取出來,在新線程中執(zhí)行操作椎镣。
     */
    
//    //使用子類 NSInvocationOperation
//    // 1.創(chuàng)建 NSInvocationOperation 對象
//    NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
//    // 2.調(diào)用 start 方法開始執(zhí)行操作
//    [op start];
//    NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
    
//    // 1.創(chuàng)建 NSBlockOperation 對象
//    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//            //在沒有使用 NSOperationQueue诈火、在主線程中單獨(dú)使用 NSBlockOperation 執(zhí)行一個(gè)操作的情況下,操作是在當(dāng)前線程執(zhí)行的状答,并沒有開啟新線程冷守。
//        }
//    }];
//    NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//    // 2.調(diào)用 start 方法開始執(zhí)行操作
//    [op start];
//    NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
    
//    /**
//    * 使用子類 NSBlockOperation
//    * 調(diào)用方法 AddExecutionBlock:
//    */
//    // 1.創(chuàng)建 NSBlockOperation 對象
//    NSBlockOperation *op = [NSBlockOperation blockOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//
//    // 2.添加額外的操作
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"5---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"6---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"7---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"8---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//
//    // 3.調(diào)用 start 方法開始執(zhí)行操作
//    [op start];
    
    /**
     NSOperationQueue 的創(chuàng)建刀崖。
     NSOperationQueue 一共有兩種隊(duì)列:主隊(duì)列、自定義隊(duì)列教沾。其中自定義隊(duì)列同時(shí)包含了串行蒲跨、并發(fā)功能译断。下邊是主隊(duì)列授翻、自定義隊(duì)列的基本創(chuàng)建方法和特點(diǎn)。
     1 主隊(duì)列
     - 凡是添加到主隊(duì)列中的操作孙咪,都會(huì)放到主線程中執(zhí)行(注:不包括操作使用addExecutionBlock:添加的額外操作堪唐,額外操作可能在其他線程執(zhí)行,感謝指正)翎蹈。
     2 自定義隊(duì)列(非主隊(duì)列)
     - 添加到這種隊(duì)列中的操作淮菠,就會(huì)自動(dòng)放到子線程中執(zhí)行。
     - 同時(shí)包含了:串行荤堪、并發(fā)功能合陵。
     */
    
//    /**
//     使用 NSOperation 子類創(chuàng)建操作,并使用 addOperation: 將操作加入到操作隊(duì)列后能夠開啟新線程澄阳,進(jìn)行并發(fā)執(zhí)行拥知。
//     */
//    // 1.創(chuàng)建自定義隊(duì)列
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//
//    // 2.創(chuàng)建操作
//    // 使用 NSInvocationOperation 創(chuàng)建操作1
//    NSInvocationOperation *op1 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task1) object:nil];
//
//    // 使用 NSInvocationOperation 創(chuàng)建操作2
//    NSInvocationOperation *op2 = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(task2) object:nil];
//
//    // 使用 NSBlockOperation 創(chuàng)建操作3
//    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [op3 addExecutionBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//
//    // 3.使用 addOperation: 添加所有操作到隊(duì)列中
//    [queue addOperation:op1]; // [op1 start]
//    [queue addOperation:op2]; // [op2 start]
//    [queue addOperation:op3]; // [op3 start]
    
//    /**
//     無需先創(chuàng)建操作,在 block 中添加操作碎赢,直接將包含操作的 block 加入到隊(duì)列中低剔。
//    使用 addOperationWithBlock: 將操作加入到操作隊(duì)列后能夠開啟新線程,進(jìn)行并發(fā)執(zhí)行肮塞。
//     */
//    // 1.創(chuàng)建隊(duì)列
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//
//    // 2.使用 addOperationWithBlock: 添加操作到隊(duì)列中
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
    
    /**
     
     NSOperationQueue 控制串行執(zhí)行襟齿、并發(fā)執(zhí)行
     NSOperationQueue 創(chuàng)建的自定義隊(duì)列同時(shí)具有串行、并發(fā)功能枕赵,上邊我們演示了并發(fā)功能猜欺,那么他的串行功能是如何實(shí)現(xiàn)的?
     這里有個(gè)關(guān)鍵屬性 maxConcurrentOperationCount拷窜,叫做最大并發(fā)操作數(shù)开皿。用來控制一個(gè)特定隊(duì)列中可以有多少個(gè)操作同時(shí)參與并發(fā)執(zhí)行。
     注意:這里 maxConcurrentOperationCount 控制的不是并發(fā)線程的數(shù)量装黑,而是一個(gè)隊(duì)列中同時(shí)能并發(fā)執(zhí)行的最大操作數(shù)副瀑。而且一個(gè)操作也并非只能在一個(gè)線程中運(yùn)行。
     
     maxConcurrentOperationCount 默認(rèn)情況下為-1恋谭,表示不進(jìn)行限制糠睡,可進(jìn)行并發(fā)執(zhí)行。
     maxConcurrentOperationCount 為1時(shí)疚颊,隊(duì)列為串行隊(duì)列狈孔。只能串行執(zhí)行信认。
     maxConcurrentOperationCount 大于1時(shí),隊(duì)列為并發(fā)隊(duì)列均抽。操作并發(fā)執(zhí)行嫁赏,當(dāng)然這個(gè)值不應(yīng)超過系統(tǒng)限制,即使自己設(shè)置一個(gè)很大的值油挥,系統(tǒng)也會(huì)自動(dòng)調(diào)整為 min{自己設(shè)定的值潦蝇,系統(tǒng)設(shè)定的默認(rèn)最大值}。
     
     */
    /**
     設(shè)置 MaxConcurrentOperationCount(最大并發(fā)操作數(shù))
     */
//    // 1.創(chuàng)建隊(duì)列
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//
//    // 2.設(shè)置最大并發(fā)操作數(shù)
//    queue.maxConcurrentOperationCount = 1; // 串行隊(duì)列
//// queue.maxConcurrentOperationCount = 2; // 并發(fā)隊(duì)列
//// queue.maxConcurrentOperationCount = 8; // 并發(fā)隊(duì)列
//
//    // 3.添加操作
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    [queue addOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"5---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
    
    /**
     NSOperation 操作依賴
     NSOperation深寥、NSOperationQueue 最吸引人的地方是它能添加操作之間的依賴關(guān)系攘乒。通過操作依賴,我們可以很方便的控制操作之間的執(zhí)行先后順序惋鹅。
     
     NSOperation提供了3個(gè)接口供我們管理和查看依賴则酝。
     - (void)addDependency:(NSOperation *)op; 添加依賴,使當(dāng)前操作依賴于操作 op 的完成闰集。
     - (void)removeDependency:(NSOperation *)op; 移除依賴沽讹,取消當(dāng)前操作對操作 op 的依賴。
     @property (readonly, copy) NSArray<NSOperation *> *dependencies; 在當(dāng)前操作開始執(zhí)行之前完成執(zhí)行的所有操作對象數(shù)組武鲁。
     
     當(dāng)然爽雄,我們經(jīng)常用到的還是添加依賴操作。現(xiàn)在考慮這樣的需求洞坑,比如說有 A盲链、B 兩個(gè)操作,其中 A 執(zhí)行完操作迟杂,B 才能執(zhí)行操作刽沾。
     
     */
    /**
     操作依賴
     使用方法:addDependency:
     
     操作結(jié)果: 通過添加操作依賴,無論運(yùn)行幾次排拷,其結(jié)果都是 op1 先執(zhí)行侧漓,op2 后執(zhí)行。
     */
//    //1 創(chuàng)建隊(duì)列
//    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//    //2 創(chuàng)建操作
//    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//       for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//    }];
//
//    //3 添加依賴
//    [op2 addDependency:op1];
//
//    //4 添加操作到隊(duì)列中
//    [queue addOperation:op1];
//    [queue addOperation:op2];
//
    
    /**
     NSOperation 優(yōu)先級
     NSOperation 提供了queuePriority(優(yōu)先級)屬性监氢,queuePriority屬性適用于同一操作隊(duì)列中的操作布蔗,不適用于不同操作隊(duì)列中的操作。默認(rèn)情況下浪腐,所有新創(chuàng)建的操作對象優(yōu)先級都是NSOperationQueuePriorityNormal纵揍。但是我們可以通過setQueuePriority:方法來改變當(dāng)前操作在同一隊(duì)列中的執(zhí)行優(yōu)先級
     // 優(yōu)先級的取值
     typedef NS_ENUM(NSInteger, NSOperationQueuePriority) {
         NSOperationQueuePriorityVeryLow = -8L,
         NSOperationQueuePriorityLow = -4L,
         NSOperationQueuePriorityNormal = 0,
         NSOperationQueuePriorityHigh = 4,
         NSOperationQueuePriorityVeryHigh = 8
     };
     對于添加到隊(duì)列中的操作,首先進(jìn)入準(zhǔn)備就緒的狀態(tài)(就緒狀態(tài)取決于操作之間的依賴關(guān)系)议街,然后進(jìn)入就緒狀態(tài)的操作的開始執(zhí)行順序(非結(jié)束執(zhí)行順序)由操作之間相對的優(yōu)先級決定(優(yōu)先級是操作對象自身的屬性)泽谨。
     
     那么,什么樣的操作才是進(jìn)入就緒狀態(tài)的操作呢?
     當(dāng)一個(gè)操作的所有依賴都已經(jīng)完成時(shí)吧雹,操作對象通常會(huì)進(jìn)入準(zhǔn)備就緒狀態(tài)骨杂,等待執(zhí)行。
     
     舉個(gè)例子雄卷,現(xiàn)在有4個(gè)優(yōu)先級都是 NSOperationQueuePriorityNormal(默認(rèn)級別)的操作:op1搓蚪,op2,op3丁鹉,op4妒潭。其中 op3 依賴于 op2,op2 依賴于 op1鳄炉,即 op3 -> op2 -> op1《虐遥現(xiàn)在將這4個(gè)操作添加到隊(duì)列中并發(fā)執(zhí)行搜骡。
     因?yàn)?op1 和 op4 都沒有需要依賴的操作拂盯,所以在 op1,op4 執(zhí)行之前记靡,就是處于準(zhǔn)備就緒狀態(tài)的操作谈竿。
     而 op3 和 op2 都有依賴的操作(op3 依賴于 op2,op2 依賴于 op1)摸吠,所以 op3 和 op2 都不是準(zhǔn)備就緒狀態(tài)下的操作空凸。

     */
    
    /**
     NSOperation、NSOperationQueue 線程間的通信在 iOS 開發(fā)過程中寸痢,我們一般在主線程里邊進(jìn)行 UI 刷新呀洲,例如:點(diǎn)擊、滾動(dòng)啼止、拖拽等事件道逗。我們通常把一些耗時(shí)的操作放在其他線程,比如說圖片下載献烦、文件上傳等耗時(shí)操作滓窍。而當(dāng)我們有時(shí)候在其他線程完成了耗時(shí)操作時(shí),需要回到主線程巩那,那么就用到了線程之間的通訊吏夯。
     */
    /**
     線程間通信
     通過線程間的通信,先在其他線程中執(zhí)行操作即横,等操作執(zhí)行完了之后再回到主線程執(zhí)行主線程的相應(yīng)操作噪生。
     */
//    //1 創(chuàng)建隊(duì)列
//    NSOperationQueue *queue = [[NSOperationQueue alloc]init];
//
//    //2 添加操作
//    [queue addOperationWithBlock:^{
//        // 異步進(jìn)行耗時(shí)操作
//        for (int i = 0; i < 2; i++) {
//            [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }
//        //回到主線程
//        [[NSOperationQueue mainQueue] addOperationWithBlock:^{
//            // 進(jìn)行一些 UI 刷新等操作
//            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        }];
//    }];
    
    /**
     NSOperation、NSOperationQueue 線程同步和線程安全
     
     線程安全:如果你的代碼所在的進(jìn)程中有多個(gè)線程在同時(shí)運(yùn)行东囚,而這些線程可能會(huì)同時(shí)運(yùn)行這段代碼跺嗽。如果每次運(yùn)行結(jié)果和單線程運(yùn)行的結(jié)果是一樣的,而且其他的變量的值也和預(yù)期的是一樣的,就是線程安全的抛蚁。
     若每個(gè)線程中對全局變量陈醒、靜態(tài)變量只有讀操作,而無寫操作瞧甩,一般來說钉跷,這個(gè)全局變量是線程安全的;若有多個(gè)線程同時(shí)執(zhí)行寫操作(更改變量)肚逸,一般都需要考慮線程同步爷辙,否則的話就可能影響線程安全。

     線程同步:可理解為線程 A 和 線程 B 一塊配合朦促,A 執(zhí)行到一定程度時(shí)要依靠線程 B 的某個(gè)結(jié)果膝晾,于是停下來,示意 B 運(yùn)行务冕;B 依言執(zhí)行血当,再將結(jié)果給 A;A 再繼續(xù)操作禀忆。
     
     */
    
    /**
     下面臊旭,我們模擬火車票售賣的方式,實(shí)現(xiàn) NSOperation 線程安全和解決線程同步問題箩退。
     場景:總共有50張火車票离熏,有兩個(gè)售賣火車票的窗口,一個(gè)是北京火車票售賣窗口戴涝,另一個(gè)是上鹤檀粒火車票售賣窗口。兩個(gè)窗口同時(shí)售賣火車票啥刻,賣完為止奸鸯。
     */
    
    /**先來看看不考慮線程安全的代碼:*/
    
//    self.ticketSurplusCount = 50;
//
//    // 1.創(chuàng)建 queue1,queue1 代表北京火車票售賣窗口
//    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
//    queue1.maxConcurrentOperationCount = 1;
//
//    // 2.創(chuàng)建 queue2,queue2 代表上海火車票售賣窗口
//    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
//    queue2.maxConcurrentOperationCount = 1;
//
//    // 3.創(chuàng)建賣票操作 op1
//    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
//        [self saleTicketNotSafe];
//    }];
//
//    // 4.創(chuàng)建賣票操作 op2
//    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//        [self saleTicketNotSafe];
//    }];
//
//    // 5.添加操作郑什,開始賣票
//    [queue1 addOperation:op1];
//    [queue2 addOperation:op2];
    
    /**
     NSOperation府喳、NSOperationQueue 線程安全
     
     線程安全解決方案:可以給線程加鎖,在一個(gè)線程執(zhí)行該操作的時(shí)候蘑拯,不允許其他線程進(jìn)行操作钝满。iOS 實(shí)現(xiàn)線程加鎖有很多種方式。@synchronized申窘、 NSLock弯蚜、NSRecursiveLock、NSCondition剃法、NSConditionLock碎捺、pthread_mutex、dispatch_semaphore、OSSpinLock收厨、atomic(property) set/ge等等各種方式晋柱。這里我們使用 NSLock 對象來解決線程同步問題。NSLock 對象可以通過進(jìn)入鎖時(shí)調(diào)用 lock 方法诵叁,解鎖時(shí)調(diào)用 unlock 方法來保證線程安全雁竞。
     */
    
//    self.ticketSurplusCount = 50;
//
//    self.lock = [[NSLock alloc] init];  // 初始化 NSLock 對象
//
//    // 1.創(chuàng)建 queue1,queue1 代表北京火車票售賣窗口
//    NSOperationQueue *queue1 = [[NSOperationQueue alloc] init];
//    queue1.maxConcurrentOperationCount = 1;
//
//    // 2.創(chuàng)建 queue2,queue2 代表上海火車票售賣窗口
//    NSOperationQueue *queue2 = [[NSOperationQueue alloc] init];
//    queue2.maxConcurrentOperationCount = 1;
//
//    // 3.創(chuàng)建賣票操作 op1
//    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
//        [self saleTicketSafe];
//    }];
//
//    // 4.創(chuàng)建賣票操作 op2
//    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//        [self saleTicketSafe];
//    }];
//
//    // 5.添加操作拧额,開始賣票
//    [queue1 addOperation:op1];
//    [queue2 addOperation:op2];
    
    
//    // 1.創(chuàng)建隊(duì)列
//    NSOperationQueue *queue = [[NSOperationQueue alloc] init];
//
//    // 2.設(shè)置最大并發(fā)操作數(shù)
////    queue.maxConcurrentOperationCount = -1; // 并行隊(duì)列
//
//    // 3.添加操作
//    NSBlockOperation *op1 = [NSBlockOperation blockOperationWithBlock:^{
//        NSLog(@"op1 start");
//        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//            NSLog(@"1---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        });
//        NSLog(@"op1 end");
//    }];
//    NSBlockOperation *op2 = [NSBlockOperation blockOperationWithBlock:^{
//        NSLog(@"op2 start");
//        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//            NSLog(@"2---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//        });
//        NSLog(@"op2 end");
//    }];
//    NSBlockOperation *op3 = [NSBlockOperation blockOperationWithBlock:^{
//        [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//        NSLog(@"3---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//    }];
//    NSBlockOperation *op4 = [NSBlockOperation blockOperationWithBlock:^{
//        [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//        NSLog(@"4---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//    }];
//    NSBlockOperation *op5 = [NSBlockOperation blockOperationWithBlock:^{
//        [NSThread sleepForTimeInterval:2]; // 模擬耗時(shí)操作
//        NSLog(@"5---%@", [NSThread currentThread]); // 打印當(dāng)前線程
//    }];
    
    //
//    [op2 addDependency:op1];
//    //
//    [queue addOperations:@[op1] waitUntilFinished:true];
//    [queue addOperations:@[op2] waitUntilFinished:true];
//    [queue addOperation:op3];
//    [queue addOperation:op4];
//    [queue addOperation:op5];
    
//    [op1 waitUntilFinished];
    //
//    [op3 cancel];
    
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末碑诉,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子侥锦,更是在濱河造成了極大的恐慌进栽,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恭垦,死亡現(xiàn)場離奇詭異快毛,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)署照,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進(jìn)店門祸泪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人建芙,你說我怎么就攤上這事《螅” “怎么了禁荸?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長阀湿。 經(jīng)常有香客問我赶熟,道長,這世上最難降的妖魔是什么陷嘴? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任映砖,我火速辦了婚禮,結(jié)果婚禮上灾挨,老公的妹妹穿的比我還像新娘邑退。我一直安慰自己,他們只是感情好劳澄,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布地技。 她就那樣靜靜地躺著,像睡著了一般秒拔。 火紅的嫁衣襯著肌膚如雪莫矗。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天,我揣著相機(jī)與錄音作谚,去河邊找鬼三娩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妹懒,可吹牛的內(nèi)容都是我干的尽棕。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼彬伦,長吁一口氣:“原來是場噩夢啊……” “哼滔悉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起单绑,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤回官,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后搂橙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體歉提,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年区转,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了苔巨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,650評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡废离,死狀恐怖侄泽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蜻韭,我是刑警寧澤悼尾,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站肖方,受9級特大地震影響闺魏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜俯画,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一析桥、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧艰垂,春花似錦泡仗、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拉宗,卻和暖如春峦树,著一層夾襖步出監(jiān)牢的瞬間辣辫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工魁巩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留急灭,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓谷遂,卻偏偏與公主長得像葬馋,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子肾扰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評論 2 349