2.多線程-GCD

1.基本概念併發

同步任務:在當前線程按順序執行,不開啓新的線程
異步任務:有開新線程的慾望
 
串行隊列:一個一個執行
並行隊列:多個任務同時執行
---------------------------------------------------------
串行隊列-同步任務 在當前線程執行
串行隊列-異步任務  開一個子線程執行
/**
    串行隊列
 
*/
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //串行隊列,同步方法
    [self serailSync];
    
    
    
    //串行隊列,異步方法
//    [self serailAsync];
}


/**
 串行隊列,異步方法
 
 1.打印順序 : 從上到下依次執行,它是串行隊列
 
 2.在哪條線程上執行:在子線程,由於它是異步執行,異步,就是不在當前線程裏面執行
 
 
 應用場景:耗時間,有順序的任務
 
    1.登陸--->2.付費--->3.才能看
 
 */
- (void)serailAsync{
    //1.建立一個串行隊列
    /**
     參數1:隊列的標識fuhao,通常是公司的域名倒寫
     www.itheima.com
     參數2:隊列的類型
     
     DISPATCH_QUEUE_SERIAL 串行隊列
     DISPATCH_QUEUE_CONCURRENT 併發隊列
     */
    dispatch_queue_t serialQueue = dispatch_queue_create("com.itheima.queue1", DISPATCH_QUEUE_SERIAL);
    
    //2.建立三個任務
    void  (^task1)  () = ^(){
        NSLog(@"task1---%@",[NSThread currentThread]);
    };
    
    void  (^task2)   () = ^(){
        NSLog(@"task2---%@",[NSThread currentThread]);
    };
    
    void  (^task3)   () = ^(){
        NSLog(@"task3---%@",[NSThread currentThread]);
    };
    
    //3.把咱們上面建立好的三個任務,添加到隊列中去,這個隊列就會本身開始調用咱們的任務
    //異步方法執行
    dispatch_async(serialQueue, task1);
    dispatch_async(serialQueue, task2);
    dispatch_async(serialQueue, task3);
}

/**
    串行隊列,同步方法
 
    1.打印順序 : 從上到下,依次打印,由於串行的
 
    2.在哪條線程上執行:主線程,由於是同步方法,因此在當前線程裏面執行,剛好當前線程是主線程,因此它就在主線程上面執行
 
 
    應用場景:開發中不多用
 
 */

- (void)serailSync{
    //1.建立一個串行隊列
    /**
        參數1:隊列的標識fuhao,通常是公司的域名倒寫
        www.itheima.com
        參數2:隊列的類型
     
            DISPATCH_QUEUE_SERIAL 串行隊列
            DISPATCH_QUEUE_CONCURRENT 併發隊列
     */
    dispatch_queue_t serialQueue = dispatch_queue_create("com.itheima.queue1", DISPATCH_QUEUE_SERIAL);
    
    //2.建立三個任務
    void  (^task1)  () = ^(){
        NSLog(@"task1---%@",[NSThread currentThread]);
    };
    
    void  (^task2)   () = ^(){
        NSLog(@"task2---%@",[NSThread currentThread]);
    };
    
    void  (^task3)   () = ^(){
        NSLog(@"task3---%@",[NSThread currentThread]);
    };
    
    //3.把咱們上面建立好的三個任務,添加到隊列中去,這個隊列就會本身開始調用咱們的任務
    //同步方法執行
    dispatch_sync(serialQueue, task1);
    dispatch_sync(serialQueue, task2);
    dispatch_sync(serialQueue, task3);
}
View Code
並行隊列-同步任務 在當前線程執行
並行隊列-異步任務 開多個子線程執行 
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //1.併發隊列,同步任務
    //[self concurrentSync];
    
    //2.併發隊列,異步任務
    [self concurrentAsync];
}

- (void)concurrentAsync{
    /**
     併發隊列,異步方法
     
     1.打印順序 :無序的
     
     2.在哪條線程上執行:在子線程上執行,第一個任務,都在它本身的線程上執行
            開N條,它是由底層可調度線程池來決定的,可調度線程池它是有一個重用機制
     
     應用場景:
        半月轉
     開頭  中間  結尾
     
     下載電視劇某一集的時候,能夠把咱們的片頭,片尾,中間內容 一塊兒下
     
     最後,拼接組合一下,就能夠播放了
     
     片頭,中間內容,片尾
     
     */
    //1.建立一個併發的隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.itheima.queue2", DISPATCH_QUEUE_CONCURRENT);
    
    //2.建立三個任務
    void  (^task1)  () = ^(){
        NSLog(@"task1---%@",[NSThread currentThread]);
    };
    
    void  (^task2)   () = ^(){
        NSLog(@"task2---%@",[NSThread currentThread]);
    };
    
    void  (^task3)   () = ^(){
        NSLog(@"task3---%@",[NSThread currentThread]);
    };
    
    
    //3.將咱們的三個任務,添加到隊列中
    //異步方法去執行
    dispatch_async(concurrentQueue, task1);
    dispatch_async(concurrentQueue, task2);
    dispatch_async(concurrentQueue, task3);
}

/**
 併發隊列,同步方法
 
 1.打印順序 : 依次執行,由於它是同步的
 
 2.在哪條線程上執行:主線程,由於它是同步方法,它就在當前線程裏面執行,主線程,依次執行
 
 當它遇到同步的時候,併發隊列,仍是依次執行
 因此說,方法的優先級會比隊列的優先級高
 
 * 只要是同步方法,都只會在當前線程裏面執行,不會開子線程
 
 應用場景:
    開發中幾乎不用
 
 */
- (void)concurrentSync{
    //1.建立一個併發的隊列
    dispatch_queue_t concurrentQueue = dispatch_queue_create("com.itheima.queue2", DISPATCH_QUEUE_CONCURRENT);
    
    //2.建立三個任務
    void  (^task1)  () = ^(){
        NSLog(@"task1---%@",[NSThread currentThread]);
    };
    
    void  (^task2)   () = ^(){
        NSLog(@"task2---%@",[NSThread currentThread]);
    };
    
    void  (^task3)   () = ^(){
        NSLog(@"task3---%@",[NSThread currentThread]);
    };

    
    //3.將咱們的三個任務,添加到隊列中
    //同步方法去執行
    dispatch_sync(concurrentQueue, task1);
    dispatch_sync(concurrentQueue, task2);
    dispatch_sync(concurrentQueue, task3);
}
View Code

 

全局隊列不須要建立,直接get就能用,執行效果與並行隊列相同
 
主線程隊列,保證操做在主線程上執行
異步任務,順序執行,會先執行後面的代碼
 
主線程隊列中存在同步任務,永遠不會被執行,被阻塞了
 主隊列只有在主線程空閒的時候,纔會去調度它裏面的任務去執行,死等
 
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{
    //1.主隊列,異步
    [self mainAsync];
    
    //2.主隊列,同步
    //[self mainSync];
}

/**
    主隊列,同步任務有問題,不能用
 
    `主隊列`只有在`主線程空閒`的時候,纔會去調度它裏面的任務去執行
 
 */
- (void)mainSync{
    //只是用來調試,說明咱們來到了這個方法
    NSLog(@"%s",__func__);
    
    //1.獲取主隊列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //2.搞三個任務
    void (^task1)  () = ^(){
        NSLog(@"task1===%@",[NSThread currentThread]);
    };
    
    void (^task2)  () = ^(){
        NSLog(@"task2===%@",[NSThread currentThread]);
    };
    
    void (^task3)  () = ^(){
        NSLog(@"task3===%@",[NSThread currentThread]);
    };
    
    //3.將任務添加到主隊列中
    dispatch_sync(mainQueue, task1);
    dispatch_sync(mainQueue, task2);
    dispatch_sync(mainQueue, task3);
    
    NSLog(@"mainSync----end");
}

/**
    主隊列,異步任務
    1.執行順序:依次執行,由於它在主線程裏面執行
    * 彷佛與咱們的異步任務有所衝突,可是由於它是主隊列,因此,只在主線程裏面執行
 
    2.是否會開線程:不會開,由於它在主線程裏面執行
 
    應用場景:
        當作了耗時間操做以後,當咱們須要回到主線程更新UI的時候,就非它不可
 */
- (void)mainAsync{
    //1.獲取主隊列
    dispatch_queue_t mainQueue = dispatch_get_main_queue();
    
    //2.搞三個任務
    void (^task1)  () = ^(){
        NSLog(@"task1===%@",[NSThread currentThread]);
    };
    
    void (^task2)  () = ^(){
        NSLog(@"task2===%@",[NSThread currentThread]);
    };
    
    void (^task3)  () = ^(){
        NSLog(@"task3===%@",[NSThread currentThread]);
    };
    
    //3.將任務添加到主隊列中
    dispatch_async(mainQueue, task1);
    dispatch_async(mainQueue, task2);
    dispatch_async(mainQueue, task3);
    
    NSLog(@"---mainAsync---end");
}
View Code

2.經常使用代碼異步

/**
        若是要在子線程中執行
     
        除了`主隊列`均可以,串行,併發,全局均可以
     
        去主線程只有一個方法,那就是主隊列,異步
     */
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //1.去子線程裏面加載圖片
        
        NSLog(@"僞裝在下載...%@",[NSThread currentThread]);
        [NSThread sleepForTimeInterval:3.0];
        
        //2.去主線程更新UI
        dispatch_async(dispatch_get_main_queue(), ^{
            NSLog(@"%@----更新UI",[NSThread currentThread]);
        });
    });

3.同步的做用async

/**
    同步的做用:保證咱們任務執行的前後順序
 
    1.登陸
 
    2.同時下載三部小電影
 */
- (void)execLongTimeOperation{
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        //1.登陸,同步在當前線程裏面工做
        dispatch_sync(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"%@----登陸...",[NSThread currentThread]);
            
            [NSThread sleepForTimeInterval:3.0];
        });
        
        //2.同時下載三部小電影(不須要前後順序)
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"downLoadA----%@",[NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"downLoadV----%@",[NSThread currentThread]);
        });
        
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            NSLog(@"downLoadI----%@",[NSThread currentThread]);
        });
    });
}

4.延遲操做ide

/**
        參數1.延時多少納秒,整個延遲3秒
        參數2:是決定,參加在哪一個線程裏面調用
        參數3:任務執行的代碼塊
     
     
        dispatch_after 異步的
     
        應用場景:
            動畫
     
     */
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"---下載完畢---%@",[NSThread currentThread]);
    });
    
    NSLog(@"---end---");

5.單例函數

+ (instancetype)sharedNetWorkToos;
static id _instance;
+ (instancetype)sharedNetWorkToos{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [[self alloc] init];
    });
    
    return _instance;
}

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _instance = [super allocWithZone:zone];
    });
    
    return _instance;
}

- (id)copyWithZone:(NSZone *)zone{
    return _instance;
}

6.調度組,組內的線程所有執行完,會到dispatch_group_notify定義的方法中oop

//1.建立一個調度組
    dispatch_group_t group = dispatch_group_create();
    
    //2.獲取全局隊列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    //3.三個下載任務
    void (^task1) () = ^(){
        NSLog(@"%@----下載片頭",[NSThread currentThread]);
    };
    
    void (^task2) () = ^(){
        NSLog(@"%@----下載中間的內容",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:5.0];
        
        NSLog(@"--下載中間內容完畢---");
    };
    
    void (^task3) () = ^(){
        NSLog(@"%@----下載片尾",[NSThread currentThread]);
    };
    
    //3.須要將咱們的隊列和任務,加入組內去監控
    dispatch_group_async(group, globalQueue, task1);
    dispatch_group_async(group, globalQueue, task2);
    dispatch_group_async(group, globalQueue, task3);
    
    //4.監聽的函數
    /**
        參數1:組
        參數2:參數3在哪一個線程裏面執行
        參數3:組內徹底下載完畢以後,須要執行的代碼
     */
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       //表示組內的全部任務都完成以後,會來到這裏
        
        NSLog(@"把下好的視頻按照順序拼接好,而後顯示在UI去播放");
    });

調度組的實現原理動畫

/**
    應用場景
    好比我去同時開三個線程下載視頻,只有當三個視頻徹底下載完畢以後,我才能作後續的事,
    這個就須要用到調度組,這個調度組,就能監聽它裏面的任務是否都執行完畢
 */
- (void)execGroupDispatch{
    //1.建立一個調度組
    dispatch_group_t group = dispatch_group_create();
    
    //2.獲取全局隊列
    dispatch_queue_t globalQueue = dispatch_get_global_queue(0, 0);
    
    //3.三個下載任務
    
    dispatch_group_enter(group); //引用計數+1
    void (^task1) () = ^(){
        NSLog(@"%@----下載片頭",[NSThread currentThread]);
        
        dispatch_group_leave(group); //引用計數-1
    };
    
    dispatch_group_enter(group); //引用計數+1
    void (^task2) () = ^(){
        NSLog(@"%@----下載中間的內容",[NSThread currentThread]);
        
        [NSThread sleepForTimeInterval:5.0];
        
        NSLog(@"--下載中間內容完畢---");
        dispatch_group_leave(group);//引用計數-1
    };
    
    void (^task3) () = ^(){
        NSLog(@"%@----下載片尾",[NSThread currentThread]);
    };
    
    //3.須要將咱們的隊列和任務,加入組內去監控
    dispatch_group_async(group, globalQueue, task1);
    dispatch_group_async(group, globalQueue, task2);
    dispatch_group_async(group, globalQueue, task3);
    
    //4.監聽的函數
    /**
        參數1:組
        參數2:參數3在哪一個線程裏面執行
        參數3:組內徹底下載完畢以後,須要執行的代碼
     */
    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
       //表示組內的全部任務都完成以後,會來到這裏
        
        NSLog(@"把下好的視頻按照順序拼接好,而後顯示在UI去播放");
    });
}
View Code

7.定時器與運行循環atom

@interface ViewController ()

@property (nonatomic, assign) int count;

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view, typically from a nib.
    
    
    [self performSelectorInBackground:@selector(subThreadRun) withObject:nil];
    
}

- (void)subThreadRun{
    NSLog(@"%s----%@",__func__,[NSThread currentThread]);
    
    //1.定義了一個定時器
    NSTimer *timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timeEvent) userInfo:nil repeats:YES];
    
    //2.將咱們的定時器加入到運行循環,只有加入到當前的運行循環裏面去,他才知道你這個時候,有一個定時任務
    /**
     NSDefaultRunLoopMode 當拖動的時候,它會停掉
     由於這種模式是互斥的
     
     forMode:UITrackingRunLoopMode 只有輸入的時候,它纔會去執行定時器任務
     
     NSRunLoopCommonModes 包含了前面兩種
     */
    //[[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode];
    //[[NSRunLoop currentRunLoop] addTimer:timer forMode:UITrackingRunLoopMode];
    
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    
    //下載,定時源事件,輸入源事件,若是放在子線程裏面,若是想要它執行任務,就必須開啓子線程的運行循環
    CFRunLoopRun();
}

- (void)timeEvent{
    self.count ++;
    
    NSLog(@"%d",self.count);
    
    if (self.count==5) {
        NSLog(@"---guale---");
        //中止當前的運行循環
        CFRunLoopStop(CFRunLoopGetCurrent());
    }
}
相關文章
相關標籤/搜索