1. 程式人生 > >iOS多線程編程(四)------ GCD(Grand Central Dispatch)

iOS多線程編程(四)------ GCD(Grand Central Dispatch)

execution 使用 att 意義 pro num patch 任務並發 comm

一、簡單介紹
是基於C語言開發的一套多線程開發機制。也是眼下蘋果官方推薦的多線程開發方法。用起來也最簡單。僅僅是它基於C語言開發,並不像NSOperation是面向對象的開發。而是全然面向過程的。假設使用GCD,全然由系統管理線程,我們不須要編寫線程代碼。僅僅需定義想要運行的任務,然後加入到適當的調度隊列(dispatch_queue).GCD會負責創建線程和調度你的任務。系統會直接提供線程管理。

二、任務和隊列
GCD中有兩個核心概念
(1)任務:運行什麽操作
(2)隊列:用來存放任務
GCD的使用就兩個步驟
(1)定制任務
(2)確定想做的事情
將任務加入到隊列中,GCD會自己主動將隊列中的任務取出,放到相應的線程中運行
提示:任務的取出遵循隊列的FIFO原則:先進先出。後進後出

三、運行任務
1、GCD中有2個用來運行任務的函數
說明:把右邊的參數(任務)提交給左邊的參數(隊列)進行運行
(1)用同步的方式運行任務 dispatch_sync(dispatch_queue_t queue, dispatch_block_t block);
參數說明:queue:隊列; block:任務
(2)用異步的方式運行任務 dispatch_async(dispatch_queue_t queue, dispatch_block_t block);

2、同步和異步的差別
同步:在當前線程中運行
異步:在還有一條線程中運行

四、隊列
1、GCD的隊列能夠分為2大類型
(1)並發隊列(Concurrent Dispatch Queue):能夠讓多個任務並發(同一時候)運行(自己主動開啟多個線程同一時候運行任務)並發功能僅僅有在異步(dispatch_async)函數才有效

(2)串行隊列(Serial Dispatch Queue):讓任務一個接著一個地運行(一個任務運行完畢後,再運行下一個任務)

2、補充說明
有4個術語比較easy混淆:同步、異步、並發、串行(在博客 多線程編程(-)—-概念 也提到了)
同步和異步決定了要不要開啟新的線程
同步:在當前線程中運行任務,不具備開啟新線程的能力
異步:在新的線程中運行任務。具備開啟新線程的能力
並發和串行決定了任務的運行方式
並發:多個任務並發運行
串行:一個任務運行完畢後,再運行下一個任務

五、(同步/異步)串行隊列和(同步/異步)並發隊列開啟線程的總結 (代碼演示樣例)
說明:
(1)同步函數不具備開啟線程的能力,不管是什麽隊列都不會開啟線程;異步函數具備開啟線程的能力,開啟幾條線程有隊列決定(串行隊列僅僅會開啟一條新的線程,並發隊列會開啟多條線程)
(2) MRC下凡是函數中。各種函數名中帶有create\copy\new\retain等字眼。都須要在不須要使用這個數據的時候進行release
ARC下GCD的數據類型不須要再作release
CF(core Foundation)的數據在ARC環境下還是須要release
(3) 異步函數具備開線程的能力,但不一定會開線程

1、異步並發隊列(同一時候開啟N個線程)
技術分享圖片

/**
 *  異步並發隊列
 */
- (void)asynchronousConcurrent{
    NSLog(@"異步函數往並發隊列中加入任務");
    NSLog(@"主線程1111 ---- %@", [NSThread currentThread]);

    // 1、創建並發隊列
    // 方法一 和創建串行隊列一樣
    //    dispatch_queue_t queue = dispatch_queue_create("asynConcurrent", DISPATCH_QUEUE_CONCURRENT);
    // 方法二 獲取全局並發隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // 2、加入任務到隊列
    dispatch_async(queue, ^{
        NSLog(@"下載圖片1 ------ %@", [NSThread currentThread]);
        [self loadImage:1];
    });

    dispatch_async(queue, ^{
        NSLog(@"下載圖片2------ %@", [NSThread currentThread]);
        [self loadImage:2];
    });

    dispatch_async(queue, ^{
        NSLog(@"下載圖片3 ------ %@", [NSThread currentThread]);
        [self loadImage:3];
    });

    NSLog(@"主線程2222 ---- %@", [NSThread currentThread]);


    // 打印結果 開啟多個線程,並發運行。

沒有先後順序(有的話也是剛好巧合而已) 能夠看number 能夠看做線程值 /** 第一次運行 2016-08-24 12:53:24.026 YJGCDDemo[1220:24092] 異步函數往並發隊列中加入任務 2016-08-24 12:53:24.027 YJGCDDemo[1220:24092] 主線程1111 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main} 2016-08-24 12:53:24.027 YJGCDDemo[1220:24092] 主線程2222 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main} 2016-08-24 12:53:24.027 YJGCDDemo[1220:24126] 下載圖片1 ------ <NSThread: 0x7f81baf25e90>{number = 2, name = (null)} 2016-08-24 12:53:24.027 YJGCDDemo[1220:24128] 下載圖片2------ <NSThread: 0x7f81bacc5fe0>{number = 3, name = (null)} 2016-08-24 12:53:24.027 YJGCDDemo[1220:24127] 下載圖片3 ------ <NSThread: 0x7f81baf81ac0>{number = 4, name = (null)} 第二次運行 2016-08-24 12:53:32.427 YJGCDDemo[1220:24092] 異步函數往並發隊列中加入任務 2016-08-24 12:53:32.427 YJGCDDemo[1220:24092] 主線程1111 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main} 2016-08-24 12:53:32.427 YJGCDDemo[1220:24092] 主線程2222 ---- <NSThread: 0x7f81bae04b90>{number = 1, name = main} 2016-08-24 12:53:32.427 YJGCDDemo[1220:24126] 下載圖片2------ <NSThread: 0x7f81baf25e90>{number = 2, name = (null)} 2016-08-24 12:53:32.427 YJGCDDemo[1220:24260] 下載圖片3 ------ <NSThread: 0x7f81baf7f660>{number = 7, name = (null)} 2016-08-24 12:53:32.427 YJGCDDemo[1220:24153] 下載圖片1 ------ <NSThread: 0x7f81baf81ac0>{number = 6, name = (null)} */ }

2、異步串行隊列(會開啟線程,可是僅僅開啟一個線程)
技術分享圖片

/**
 *  異步串行隊列
 */
- (void)asynchronousSerial{

    NSLog(@"用異步函數往串行隊列中加入任務");
    NSLog(@"主線程1111 ---- %@", [NSThread currentThread]);

    //1. 創建串行隊列
    dispatch_queue_t queue = dispatch_queue_create("asynSerial", DISPATCH_QUEUE_SERIAL);
    dispatch_async(queue, ^{
        NSLog(@"下載圖片1 --- %@", [NSThread currentThread]);
        [self loadImage:1];
    });


    dispatch_async(queue, ^{
        NSLog(@"下載圖片2 --- %@", [NSThread currentThread]);
        [self loadImage:2];
    });

    dispatch_async(queue, ^{
        NSLog(@"下載圖片3 --- %@", [NSThread currentThread]);
        [self loadImage:3];
    });

    NSLog(@"主線程2222 ---- %@", [NSThread currentThread]);


    // 打印結果:異步串行隊列,會開啟一個線程,順序運行。 看運行結果也能夠看出,圖片是一張下載完在下載下一張的。

/** 2016-08-24 12:39:14.195 YJGCDDemo[942:16507] 用異步函數往串行隊列中加入任務 2016-08-24 12:39:14.196 YJGCDDemo[942:16507] 主線程1111 ---- <NSThread: 0x7f8ed1f05f90>{number = 1, name = main} 2016-08-24 12:39:14.196 YJGCDDemo[942:16507] 主線程2222 ---- <NSThread: 0x7f8ed1f05f90>{number = 1, name = main} 2016-08-24 12:39:14.196 YJGCDDemo[942:16622] 下載圖片1 --- <NSThread: 0x7f8ed1c43c00>{number = 2, name = (null)} 2016-08-24 12:39:14.261 YJGCDDemo[942:16622] 下載圖片2 --- <NSThread: 0x7f8ed1c43c00>{number = 2, name = (null)} 2016-08-24 12:39:14.303 YJGCDDemo[942:16622] 下載圖片3 --- <NSThread: 0x7f8ed1c43c00>{number = 2, name = (null)} */ }

3、同步並發隊列(不會開啟新的線程,並發隊列失去並發的功能)
技術分享圖片

/**
 *  同步並發隊列
 */
- (void)synchronousConcurrent{
        NSLog(@"用同步函數往並發隊列中加入任務");
        NSLog(@"主線程1111 ---- %@", [NSThread currentThread]);

        // 1.創建並發隊列
        // 方式一 一般都使用這樣的方式獲取
    //    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

        // 方式二 和創建串行隊列一樣
        dispatch_queue_t queue = dispatch_queue_create("syncConcurrentncy", DISPATCH_QUEUE_CONCURRENT);

        // 2.加添任務到隊列
        dispatch_sync(queue, ^{
            NSLog(@"下載圖片1 ---- %@", [NSThread currentThread]);
            [self loadImage:1];
        });

        dispatch_sync(queue, ^{
            NSLog(@"下載圖片2 ---- %@", [NSThread currentThread]);
            [self loadImage:2];
        });

        dispatch_sync(queue, ^{
            NSLog(@"下載圖片3 ---- %@", [NSThread currentThread]);
            [self loadImage:3];
        });

        NSLog(@"主線程2222 ---- %@", [NSThread currentThread]);


    // 打印結果 和同步串行隊列一樣 這邊並發隊列效果失效,不會開啟線程。
    /**
     2016-08-24 11:20:44.153 YJGCDDemo[43913:3002870] 用同步函數往並發隊列中加入任務
     2016-08-24 11:20:44.154 YJGCDDemo[43913:3002870] 主線程1111 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.154 YJGCDDemo[43913:3002870] 下載圖片1 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.433 YJGCDDemo[43913:3002870] 下載圖片2 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.475 YJGCDDemo[43913:3002870] 下載圖片3 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}
     2016-08-24 11:20:44.508 YJGCDDemo[43913:3002870] 主線程2222 ---- <NSThread: 0x7f8742604eb0>{number = 1, name = main}

     */
}

4、同步串行隊列(不會開啟新的線程)
技術分享圖片

/**
 *  同步串行隊列
 */
- (void)synchronousSerial{
    NSLog(@"用同步函數往串行隊列中加入任務");
    NSLog(@"主線程111----- %@", [NSThread currentThread]);

    // 1、創建串行隊列 // DISPATCH_QUEUE_SERIAL 串行隊列 也能夠為NULL NULL時 默認是 串行隊列; DISPATCH_QUEUE_CONCURRENT 並發隊列
    dispatch_queue_t queue = dispatch_queue_create("syncSerial", DISPATCH_QUEUE_SERIAL);

    // 2、加入任務到隊列中運行
    dispatch_sync(queue, ^{
        // 此塊代碼沒有不論什麽意義,僅僅是為了體現同步串行隊列的效果, 僅僅能運行完了 才幹運行面下的。
        for (int i = 0; i < 30000; i++) {
            //
            NSLog(@"%i", i);
        }
        NSLog(@"下載圖片1 ---- %@", [NSThread currentThread]);
        [self loadImage:1];
    });

    dispatch_sync(queue, ^{
        for (int i = 0; i < 30000; i++) {
            //
            NSLog(@"%i", i);
        }
        NSLog(@"下載圖片2 -- %@", [NSThread currentThread]);
        [self loadImage:2];
    });

    dispatch_sync(queue, ^{
        for (int i = 0; i < 30000; i++) {
            //
            NSLog(@"%i", i);
        }
        NSLog(@"下載圖片3 -- %@", [NSThread currentThread]);
        [self loadImage:3];
    });

    NSLog(@"主線程222----- %@", [NSThread currentThread]);


    // 打印結果就是同步按順序運行。

每一個任務按順序運行,不開啟線程 /** 2016-08-24 10:52:55.049 YJGCDDemo[43413:2986818] 用同步函數往串行隊列中加入任務 2016-08-24 10:52:55.049 YJGCDDemo[43413:2986818] 主線程111----- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main} 2016-08-24 10:52:55.050 YJGCDDemo[43413:2986818] 下載圖片1 ---- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main} 2016-08-24 10:52:55.580 YJGCDDemo[43413:2986818] 下載圖片2 -- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main} 2016-08-24 10:52:55.616 YJGCDDemo[43413:2986818] 下載圖片3 -- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main} 2016-08-24 10:52:55.644 YJGCDDemo[43413:2986818] 主線程222----- <NSThread: 0x7fd5dbc00dd0>{number = 1, name = main} */ }

六、經常用法 在Demo中的CommonMethodsViewCotroller類

1、後臺運行

 dispatch_async(dispatch_get_global_queue(0, 0), ^{
        // something
    });

2、主線程運行

dispatch_async(dispatch_get_main_queue(), ^{
        // something
    });

3、一次性運行 dispatch_once()

static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // code to be execution once
        NSLog(@"改行代碼僅僅運行一次");
    });

4、延遲N秒運行 (這邊列舉了四種方式) dispatch_time()
技術分享圖片

    NSLog(@"打印線程----- %@", [NSThread currentThread]);
    // 延時運行方式一 使用NSObject的方法
    // 2秒後再調用self的run方法
//    [self performSelector:@selector(loadImage) withObject:nil afterDelay:2.0];

    // 延遲運行方式二 使用GCD函數
       // 在同步函數中運行
        // 註意 假設使用異步函數 dispatch_async 那麽[self performSelector:@selector(loadImage) withObject:nil afterDelay:5.0]; 不會被運行
//    dispatch_queue_t queue = dispatch_queue_create("yangjian.net.cn", 0);
//    dispatch_sync(queue, ^{
//        [self performSelector:@selector(loadImage) withObject:nil afterDelay:2.0];
//    });

    // 延遲運行方式三 能夠安排其線程---> 主隊列
//    dispatch_queue_t queue = dispatch_get_main_queue();
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC)), queue, ^{
//        NSLog(@"主隊列--延遲運行------%@",[NSThread currentThread]);
//        [self gcdLoadImage];
//    });

    // 延遲運行方式四 能夠安排其線程---> 並發隊列
    //1、獲取全局並發隊列
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    //2、計算任務運行的時間
    dispatch_time_t when = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5.0 * NSEC_PER_SEC));
    //3、會在when這個時間點。運行queue中的這個任務
    dispatch_after(when, queue, ^{
        NSLog(@"並發隊列--延遲運行 ---- %@", [NSThread currentThread]);
        [self gcdLoadImage];
    });

5、運行某個代碼片段N次 dispatch_apply()

dispatch_queue_t globalQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_apply(4, globalQueue, ^(size_t index) {
        // 運行4次
    });

6、隊列組的使用 dispatch_group_async()

    // 需求
    //1  分別異步運行2個耗時的操作
    //2  等兩個異步操作都運行完畢後,再回到主線程運行操作

//   假設想要高速高效地實現上述需求,能夠考慮用隊列組

    dispatch_group_t group = dispatch_group_create();
    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        // 並發運行的線程一
    });

    dispatch_group_async(group, dispatch_get_global_queue(0, 0), ^{
        // 並發運行的線程二
    });

    dispatch_group_notify(group, dispatch_get_global_queue(0, 0), ^{
        // 等前面的異步操作都運行完畢後,回到主線程
    });

6、dispatch_barrier_async()

/**
 *   使用此方法創建的任務首先會查看隊列中有沒有別的任務要運行。假設有。則會等待已有任務運行完畢再運行;同一時候在此方法後加入的任務必須等待此方法中任務運行後才幹運行。(利用這種方法能夠控制運行順序,比如前面先載入最後一張圖片的需求就能夠先使用這種方法將最後一張圖片載入的操作加入到隊列,然後調用dispatch_async()加入其它圖片載入任務)
 */
- (void)barrier{
//    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    dispatch_queue_t queue = dispatch_queue_create("RICHARD", DISPATCH_QUEUE_CONCURRENT); // 創建並發隊列

    dispatch_async(queue, ^{
        NSLog(@"下載圖片1 --- %@", [NSThread currentThread]);
        UIImage *image = [self requestImageData:@"http://atth.eduu.com/album/201203/12/1475134_1331559643qMzc.jpg"];

        // 回到主線程
        dispatch_queue_t mainQueue1 = dispatch_get_main_queue();
        dispatch_async(mainQueue1, ^{
            self.imageViewOne.image = image;
        });
    });

    dispatch_async(queue, ^{
        NSLog(@"下載圖片2 --- %@", [NSThread currentThread]);
    });

    dispatch_barrier_async(queue, ^{
        NSLog(@"dispatch_barrier_async 下載圖片3  --- %@", [NSThread currentThread]);
        UIImage *image = [self requestImageData:@"http://5.26923.com/download/pic/000/335/06efd7b7d40328f1470d4fd99a214243.jpg"];
        dispatch_async(main_queue, ^{
            self.imageViewThree.image = image;
        });
    });



    dispatch_async(queue, ^{
        NSLog(@"下載圖片4 --- %@", [NSThread currentThread]);
    });

    dispatch_async(queue, ^{
        NSLog(@"下載圖片5 --- %@", [NSThread currentThread]);
        UIImage *image = [self requestImageData:@"http://h.hiphotos.baidu.com/image/pic/item/dc54564e9258d109a4d1165ad558ccbf6c814d23.jpg"];
        dispatch_async(main_queue, ^{
            self.imageViewTwo.image = image;
        });
    });


    // 打印結果分析:1、 12 運行玩完 運行3  再運行45  2、 12順序不定 45順序不定
    /**
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367515] 下載圖片2 --- <NSThread: 0x7ff720f13ac0>{number = 47, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367518] 下載圖片1 --- <NSThread: 0x7ff720e1c5d0>{number = 48, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367102] dispatch_barrier_async 下載圖片3  --- <NSThread: 0x7ff720d9b7d0>{number = 43, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367293] 下載圖片4 --- <NSThread: 0x7ff720c9e250>{number = 45, name = (null)}
     2016-08-25 10:47:00.560 YJGCDDemo[4436:367516] 下載圖片5 --- <NSThread: 0x7ff720dba290>{number = 46, name = (null)}

     */



}

七、代碼演示樣例 在Demo中的CommonMethodsViewCotroller類
下載兩張照片完後,合並照片。

(兩種方式)
技術分享圖片

/**
 *  合並圖片(方式一)
 */
- (void)mergeImage{

//    // 獲取全局並發隊列
//    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
//
//    // 獲取主隊列
//    dispatch_queue_t mainQueue = dispatch_get_main_queue();


    dispatch_async(global_queue, ^{
        // 下載圖片1
        UIImage *image1 = [self requestImageData:@"http://h.hiphotos.baidu.com/image/pic/item/dc54564e9258d109a4d1165ad558ccbf6c814d23.jpg"];
        NSLog(@"圖片1下載完畢----%@", [NSThread currentThread]);

        UIImage *image2 = [self requestImageData:@"http://5.26923.com/download/pic/000/335/06efd7b7d40328f1470d4fd99a214243.jpg"];

        NSLog(@"圖片2下載完畢----%@", [NSThread currentThread]);

        // 回到主線程顯示圖片
        dispatch_async(main_queue, ^{
            NSLog(@"顯示圖片---%@", [NSThread currentThread]);
            self.imageViewOne.image = image1;
            self.imageViewTwo.image = image2;

            // 合並兩張圖片
            UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0.0);
            [image1 drawAsPatternInRect:CGRectMake(0, 0, 100, 200)];
            [image2 drawAsPatternInRect:CGRectMake(100, 0, 100, 200)];
            self.imageViewThree.image = UIGraphicsGetImageFromCurrentImageContext();
            // 關閉上下文
            UIGraphicsEndImageContext();

            NSLog(@"圖片合並完畢----%@", [NSThread currentThread]);

        });
    });

    // 打印結果 須要等圖片1下載完 在下載圖片2 再回到主線程
    /**
     2016-08-24 16:58:40.123 YJGCDDemo[3480:125975] 圖片1下載完畢----<NSThread: 0x7ff65acd85f0>{number = 3, name = (null)}
     2016-08-24 16:58:40.319 YJGCDDemo[3480:125975] 圖片2下載完畢----<NSThread: 0x7ff65acd85f0>{number = 3, name = (null)}
     2016-08-24 16:58:40.319 YJGCDDemo[3480:125910] 顯示圖片---<NSThread: 0x7ff65ad00dd0>{number = 1, name = main}
     2016-08-24 16:58:40.335 YJGCDDemo[3480:125910] 圖片合並完畢----<NSThread: 0x7ff65ad00dd0>{number = 1, name = main}
     */
    // 效率不高 須要等圖片1,圖片2都下載完了後才合並
    // 優化 使用隊列組能夠讓圖片1 圖片2的下載任務同事進行,且當兩個下載任務都完畢的時候回到主線程進行顯示。


}
/**
 *  使用隊列組解決(方式二)
 */
- (void)groupMergeImage{
    //步驟
    //  1、創建一個隊列組
    //  2、開啟一個任務下載圖片1
    //  3、開啟一個任務下載圖片2
    //  同一時候運行下載圖片1  和 下載圖片2操作
    //  4、等group中的全部任務都運行完畢,再回到主線程運行其它操作

    // 1、創建一個隊列租
    dispatch_group_t group = dispatch_group_create();

    // 2、開啟一個任務下載圖片1
    __block UIImage *image1 = nil;
    dispatch_group_async(group, global_queue, ^{
        image1 = [self requestImageData:@"http://h.hiphotos.baidu.com/image/pic/item/dc54564e9258d109a4d1165ad558ccbf6c814d23.jpg"];
        NSLog(@"圖片1下載完畢--- %@", [NSThread currentThread]);
    });

    // 3、開啟一個任務下載圖片2
    __block UIImage *image2 = nil;
    dispatch_group_async(group, global_queue, ^{
        image2 = [self requestImageData:@"http://5.26923.com/download/pic/000/335/06efd7b7d40328f1470d4fd99a214243.jpg"];
        NSLog(@"圖片2下載完畢--- %@", [NSThread currentThread]);
    });

    // 同一時候運行下載圖片1\下載圖片2操作

    // 4、等group重的全部任務都運行完畢,再回到主線程運行其它操作
    dispatch_group_notify(group, main_queue, ^{
        NSLog(@"顯示圖 --- %@", [NSThread currentThread]);
        self.imageViewOne.image = image1;
        self.imageViewTwo.image = image2;

        // 合並兩張圖片
        UIGraphicsBeginImageContextWithOptions(CGSizeMake(200, 200), NO, 0.0);
        [image1 drawAsPatternInRect:CGRectMake(0, 0, 100, 200)];
        [image2 drawAsPatternInRect:CGRectMake(100, 0, 100, 200)];
        self.imageViewThree.image = UIGraphicsGetImageFromCurrentImageContext();
        // 關閉上下文
        UIGraphicsEndImageContext();

        NSLog(@"圖片合並完畢 --- %@", [NSThread currentThread]);
    });


    // 同一時候開啟兩個線程 分別下載圖片 會比上面的效率高一點
    /**
     2016-08-24 16:58:03.785 YJGCDDemo[3467:125346] 圖片1下載完畢--- <NSThread: 0x7f8d13cc61c0>{number = 3, name = (null)}
     2016-08-24 16:58:03.978 YJGCDDemo[3467:125349] 圖片2下載完畢--- <NSThread: 0x7f8d13ece620>{number = 4, name = (null)}
     2016-08-24 16:58:03.978 YJGCDDemo[3467:125303] 顯示圖 --- <NSThread: 0x7f8d13e052b0>{number = 1, name = main}
     2016-08-24 16:58:03.995 YJGCDDemo[3467:125303] 圖片合並完畢 --- <NSThread: 0x7f8d13e052b0>{number = 1, name = main}

     */

}

八、線程間通信

// 從子線程回到主線程
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     // 運行耗時的異步操作
     dispatch_async(dispatch_get_main_queue(), ^{
          //回到主線程,運行UI刷新操作
     });
 });

九、Operation和GCD的對照
長處: 不須要關心線程管理。數據同步的問題;
差別:
1、性能:GCD更接近底層。而NSOperation則更高級抽象,所以GCD在追求性能的底層操作來說。是速度最快的。
2、從異步操作之間的事務性,順序行,依賴關系。GCD須要自己寫很多其它的代碼來實現,而NSOperationQueue已經內建了這些支持
3、假設異步操作的國學很多其它的被交互和UI呈現出來,NSOperationQueue會是一個更好的選擇。

底層代碼中。任務之間不太互相依賴。而須要更高的並發能力,GCD則更有優勢

十、總結
學了兩天,對gcd有一些的了解。能在項目中使用多線程。只是這邊也要避免非常多死鎖的問題,後期有時間再整理出來。四天左右把iOS多線程的幾種方法都整理了下,寫了demo。也算對自己的一個小小總結。


總結兩點:
1、線程運行方式
dispatch_async 異步運行
dispatch_sync 同步運行
dispatch_delay 延遲運行
2、處理任務對象
dispatch_get_main_queue 主線程隊列(UI線程隊列)
dispatch_get_global_queue 並發線程隊列
串行隊列

demo地址: http://download.csdn.net/detail/yj229201093/9611939
參考鏈接:http://www.cnblogs.com/wendingding/p/3806821.html
感謝各路大神的博客,學無止境…

iOS多線程編程(四)------ GCD(Grand Central Dispatch)