天天看點

block并行程式設計與線程同步

block可用于并行程式設計,線程同步多用信号量來實作,保證執行完一個block再執行下一個block,當所有block都執行完畢後,再利用group,通知執行後面的代碼,使用wait(阻塞線程),或notify(不阻塞線程)等待執行結束後再繼續。

#import <UIKit/UIKit.h>
#import "AppDelegate.h"

static volatile BOOL flag = NO;
//一個定義為volatile的變量是說這變量可能會被意想不到地改變,這樣,編譯器就不會去假設這個變量的值了。精确地說就是,優化器在用到這個變量時必須每次都小心地重新讀取這個變量的值,而不是使用儲存在寄存器裡的備份。
static const int Length = 1000000000;
static int Data[Length];
static void initData() {
    for (int i = 0; i < Length; i++) {
        Data[i] = i+1;
    }
}

#define Test_Mode 7

int main(int argc, char * argv[]) {
    @autoreleasepool {
#if Test_Mode == 0
        //local block
        void (^myBlock) (void) = ^(void){
            NSLog(@"I am a block");
        };
        myBlock = ^{
            NSLog(@"I have been changed");
        };
        myBlock();
#elif Test_Mode == 1
        //array block
        void (^blockArray[2])(void) = {
            ^{
                NSLog(@"block 1");
            },
            ^{
                NSLog(@"block 2");
            },
        };
        blockArray[0]();
        blockArray[1]();
        //block該如何修改外部變量呢?有兩種辦法,第一種是可以修改 static 全局變量;第二種是可以修改用關鍵字 __block 修飾的變量。
#elif Test_Mode == 2
        //block recursion(遞歸) (必須用static或__blcok修飾block)
        void (^aBlock)(int);
        static void (^ const recursionBlock)(int) = ^(int i){
            if (i > 0) {
                NSLog(@"recursionBlock %d", i);
                recursionBlock(i - 1);
            }
        };
        aBlock = recursionBlock;
        aBlock(6);
//        recursionBlock(5);
        
        __block void (^recursionBlock2)(int) = ^(int i){
            if (i > 0) {
                NSLog(@"recursionBlock2 %d", i);
                recursionBlock2(i - 1);
            }
        };
        aBlock = recursionBlock2;
        aBlock(10);
//        recursionBlock2(8);
#elif Test_Mode == 3
        //dispatch block
        initData();//調用初始化函數
        void (^dispatchBlcok)(void) = ^{
            int sum = 0;
            for (int i = 0; i < Length; i++) {
                sum += Data[i];
            }
            NSLog(@"sum = %d", sum);
            flag = YES;
        };
        dispatch_queue_t queue = dispatch_queue_create("dispatch block", 0);
        dispatch_async(queue, dispatchBlcok);
        while (!flag);//阻塞線程,直到執行完block(非必需)(用信号量實作更好)
        
#elif Test_Mode == 4
        //semaphore(信号量)
        initData();
        //不用block修飾好像也可以。
        __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);//0表示資源未準備好,需要等待
        dispatch_queue_t queue = dispatch_queue_create("semaphore block", 0);
        dispatch_async(queue, ^{
            int sum = 0;
            for (int i = 0; i < Length; i++)
                sum += Data[i];
            NSLog(@"sum = %d", sum);
            dispatch_semaphore_signal(sem);//準備好了,增加 semaphore 計數
        });
        NSLog(@"<<semaphore<<");
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);//可以繼續執行了,減少 semaphore 計數
#elif Test_Mode == 5
        //多個信号量.按照 FIFO 順序執行并用 semaphore 線程同步。
        initData();
        __block int sum = 0;
        __block dispatch_semaphore_t sem = dispatch_semaphore_create(0);
        __block dispatch_semaphore_t taskSem = dispatch_semaphore_create(0);
        dispatch_queue_t queue = dispatch_queue_create("semaphore block", 0);
        dispatch_block_t task1 = ^{
            int s = 0;
            for (int
                 i = 0; i < Length; i ++) {
                s += Data[i];
            }
            sum = s;
            NSLog(@">> sfter add : %d", sum);
            dispatch_semaphore_signal(taskSem);
        };
        dispatch_block_t task2 = ^{
            dispatch_semaphore_wait(taskSem, DISPATCH_TIME_FOREVER);
            int s = sum;
            for (int  i = 0; i < Length; i++) {
                s -= Data[i];
            }
            sum = s;
            NSLog(@"<< after subtract: %d", sum);
            dispatch_semaphore_signal(sem);
        };
        dispatch_async(queue, task1);
        dispatch_async(queue, task2);
        dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
        NSLog(@"after 5");
#elif Test_Mode == 6
        //dispatch_apply 進行并發疊代。這麼做與 for 循環相比有什麼好處呢?答案是:并行,這裡的求和是并行的,并不是按照順序依次執行求和的。
        initData();
        dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
        __block int sum = 0;
        __block int *pArray = Data;
        dispatch_apply(Length, queue, ^(size_t i) {
            //循環體的執行是并發執行的。
            sum += pArray[i];
            NSLog(@"pArray :%zu", i);
        });
        NSLog(@">> sum :%d", sum);
#elif Test_Mode == 7
        //<span style="font-family: Arial, Helvetica, sans-serif;">dispatch_queue</span>
        initData();
        __block  int sum = 0;
        __block dispatch_semaphore_t taskSem = dispatch_semaphore_create(0);
        dispatch_queue_t queue = dispatch_queue_create("group queue", 0);
        dispatch_group_t group = dispatch_group_create();
        dispatch_block_t task1 = ^{
            int s = 0;
            for (int i = 0; i < Length; i++) {
                s += Data[i];
            }
            sum = s;
            NSLog(@">>after add:%d", sum);
            dispatch_semaphore_signal(taskSem);
        };
        
        dispatch_block_t task2 = ^{
            dispatch_semaphore_wait(taskSem, DISPATCH_TIME_FOREVER);
            int s = sum;
            for (int i = 0; i < Length; i++) {
                s -= Data[i];
            }
            sum = s;
            NSLog(@">> after subtract:%d", sum);
        };
        dispatch_group_async(group, queue, task1);
        dispatch_group_async(group, queue, task2);
        
        dispatch_group_wait(group, DISPATCH_TIME_FOREVER);//保證group裡的所有任務執行完後再往下執行。會阻塞目前線程。
        NSLog(@"after group wait");
        dispatch_group_notify(group, queue, ^{//在group執行完所有任務後執行,具體執行的時間不确定。
            NSLog(@"group over");
        });
        
#endif
        NSLog(@"<<<<<<Test Done>>>>>>");
        
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}