美文网首页多线程 & 网络
iOS多线程:GCD进阶篇

iOS多线程:GCD进阶篇

作者: 码小菜 | 来源:发表于2020-04-12 23:37 被阅读0次
别墅

目录
一,dispatch_after
二,dispatch_once
三,dispatch_apply
四,dispatch_group
五,dispatch_semaphore
六,dispatch_barrier

一,dispatch_after

1,使用

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"1");
    
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"2");
    });
    
    NSLog(@"3");
}

// 打印
07:04:14.185538+0800 Demo[82191:12753573] 1
07:04:14.186943+0800 Demo[82191:12753573] 3
07:04:17.185982+0800 Demo[82191:12753573] 2

2,说明

  • 它的作用是延迟执行任务

  • 并不是在指定时间开始执行任务,而是在指定时间将任务添加到队列中

  • 因为队列可能会出现阻塞的情况,所以任务的执行时间不一定准确

二,dispatch_once

1,使用

- (void)viewDidLoad {
    [super viewDidLoad];
    
    for (int i = 0; i < 5; i++) {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            NSLog(@"%d", i + 1);
        });
    }
}

// 打印
1

2,说明

  • 它的作用是保证任务只执行一次

  • 它能够保证任务的执行是线程安全的

  • 它常应用于单例模式

三,dispatch_apply

1,使用

- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"begin");
    
    NSArray *array = @[@"1", @"2", @"3"];
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_apply(array.count, queue, ^(size_t index) {
        NSLog(@"%@---%@", array[index], [NSThread currentThread]);
    });
    
    NSLog(@"end");
}

// 打印
begin
2---<NSThread: 0x6000019498c0>{number = 3, name = (null)}
3---<NSThread: 0x60000194a080>{number = 4, name = (null)}
1---<NSThread: 0x600001916100>{number = 1, name = main}
end

2,说明

  • 它的作用是快速遍历一组数据,类似于for循环

  • for循环是逐个进行遍历,而它能够在多个线程中同时遍历多个数据

  • 它按指定的次数将任务添加到队列中,并等待所有的任务执行完毕才返回

四,dispatch_group

1,使用

  • dispatch_group_notify
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"begin---%@", [NSThread currentThread]);
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务1---%@", [NSThread currentThread]);
        sleep(1.0);
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2---%@", [NSThread currentThread]);
        sleep(1.0);
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务3---%@", [NSThread currentThread]);
        sleep(1.0);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"group中的任务都已经执行完毕---%@", [NSThread currentThread]);
    });
    
    NSLog(@"end---%@", [NSThread currentThread]);
}

// 打印
begin---<NSThread: 0x60000289e800>{number = 1, name = main}
end---<NSThread: 0x60000289e800>{number = 1, name = main}
任务1---<NSThread: 0x6000028cc980>{number = 4, name = (null)}
任务3---<NSThread: 0x6000028c6040>{number = 3, name = (null)}
任务2---<NSThread: 0x6000028c2100>{number = 5, name = (null)}
group中的任务都已经执行完毕---<NSThread: 0x60000289e800>{number = 1, name = main}
  • dispatch_group_wait
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"begin---%@", [NSThread currentThread]);
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务1---%@", [NSThread currentThread]);
        sleep(1.0);
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务2---%@", [NSThread currentThread]);
        sleep(1.0);
    });

    dispatch_group_async(group, queue, ^{
        NSLog(@"任务3---%@", [NSThread currentThread]);
        sleep(1.0);
    });

    dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
    NSLog(@"group中的任务都已经执行完毕---%@", [NSThread currentThread]);
    
    NSLog(@"end---%@", [NSThread currentThread]);
}

// 打印
begin---<NSThread: 0x6000029fcbc0>{number = 1, name = main}
任务2---<NSThread: 0x6000029a1780>{number = 6, name = (null)}
任务1---<NSThread: 0x60000298d400>{number = 7, name = (null)}
任务3---<NSThread: 0x6000029b3b40>{number = 4, name = (null)}
group中的任务都已经执行完毕---<NSThread: 0x6000029fcbc0>{number = 1, name = main}
end---<NSThread: 0x6000029fcbc0>{number = 1, name = main}
  • dispatch_group_enterdispatch_group_leave
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"begin---%@", [NSThread currentThread]);
    
    dispatch_group_t group = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);

    // group中的任务数+1
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"任务1---%@", [NSThread currentThread]);
        sleep(1.0);
        // group中的任务数-1
        dispatch_group_leave(group);
    });

    // group中的任务数+1
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"任务2---%@", [NSThread currentThread]);
        sleep(1.0);
        // group中的任务数-1
        dispatch_group_leave(group);
    });

    // group中的任务数+1
    dispatch_group_enter(group);
    dispatch_async(queue, ^{
        NSLog(@"任务3---%@", [NSThread currentThread]);
        sleep(1.0);
        // group中的任务数-1
        dispatch_group_leave(group);
    });

    dispatch_group_notify(group, dispatch_get_main_queue(), ^{
        NSLog(@"group中的任务数为0---%@", [NSThread currentThread]);
    });
    
    NSLog(@"end---%@", [NSThread currentThread]);
}

// 打印
begin---<NSThread: 0x600002d960c0>{number = 1, name = main}
end---<NSThread: 0x600002d960c0>{number = 1, name = main}
任务1---<NSThread: 0x600002dfb100>{number = 7, name = (null)}
任务3---<NSThread: 0x600002ddbc40>{number = 4, name = (null)}
任务2---<NSThread: 0x600002dc3400>{number = 5, name = (null)}
group中的任务数为0---<NSThread: 0x600002d960c0>{number = 1, name = main}

2,说明

  • dispatch_group_wait会阻塞当前线程

  • dispatch_group_enterdispatch_group_leave的作用等同于dispatch_group_async

  • 它常应用于多个接口都请求完毕后再刷新UI

五,dispatch_semaphore

1,使用

  • 将异步执行任务转换为同步执行任务
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"1---%@", [NSThread currentThread]);
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1.0);
        NSLog(@"2---%@", [NSThread currentThread]);
        dispatch_semaphore_signal(semaphore);
    });

    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    NSLog(@"3---%@", [NSThread currentThread]);
}

// 打印
1---<NSThread: 0x600002491080>{number = 1, name = main}
2---<NSThread: 0x6000024c0e80>{number = 5, name = (null)}
3---<NSThread: 0x600002491080>{number = 1, name = main}
  • 控制线程的最大并发数
- (void)viewDidLoad {
    [super viewDidLoad];
    
    // 最大并发数为2
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(2);

    for (int i = 0; i < 10; i++) {
        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
            dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);

            sleep(1.0);
            NSLog(@"%d---%@", i + 1, [NSThread currentThread]);

            dispatch_semaphore_signal(semaphore);
        });
    }
}

// 打印(同时只打印两次)
06:43:09.900220+0800 Demo[26085:16867256] 1---<NSThread: 0x6000006e2140>{number = 6, name = (null)}
06:43:09.900238+0800 Demo[26085:16867255] 2---<NSThread: 0x6000006fff80>{number = 5, name = (null)}
06:43:10.903011+0800 Demo[26085:16867239] 3---<NSThread: 0x6000006e7900>{number = 3, name = (null)}
06:43:10.903022+0800 Demo[26085:16867243] 4---<NSThread: 0x6000006c7c40>{number = 8, name = (null)}
06:43:11.906306+0800 Demo[26085:16867240] 5---<NSThread: 0x6000006b8600>{number = 4, name = (null)}
06:43:11.906350+0800 Demo[26085:16867259] 6---<NSThread: 0x6000006c7f40>{number = 9, name = (null)}
06:43:12.907370+0800 Demo[26085:16867261] 7---<NSThread: 0x6000006c7f80>{number = 11, name = (null)}
06:43:12.907366+0800 Demo[26085:16867260] 8---<NSThread: 0x6000006cf140>{number = 10, name = (null)}
06:43:13.912833+0800 Demo[26085:16867262] 9---<NSThread: 0x6000006c7d00>{number = 13, name = (null)}
06:43:13.912925+0800 Demo[26085:16867263] 10---<NSThread: 0x6000006caac0>{number = 12, name = (null)}
  • 把初始值设置为1,代表同一时间只允许一个线程访问资源,这样它就能起到锁的作用
@interface ViewController ()
@property (nonatomic, assign) int ticket;
@property (nonatomic, strong) dispatch_semaphore_t semaphore;
@end

@implementation ViewController
- (void)viewDidLoad {
    [super viewDidLoad];
    
    self.ticket = 10;
    self.semaphore = dispatch_semaphore_create(1);
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
    
    dispatch_async(queue, ^{
        for (int i = 0; i < 5; i++) {
            [self saleTicket];
        }
    });
}
- (void)saleTicket {
    dispatch_semaphore_wait(self.semaphore, DISPATCH_TIME_FOREVER);
    
    int oldTicket = self.ticket;
    sleep(0.3);
    oldTicket -= 1;
    self.ticket = oldTicket;
    NSLog(@"剩余%d张票---%@", self.ticket, [NSThread currentThread]);
    
    dispatch_semaphore_signal(self.semaphore);
}
@end

// 打印
剩余9张票---<NSThread: 0x60000324a980>{number = 3, name = (null)}
剩余8张票---<NSThread: 0x600003253b80>{number = 5, name = (null)}
剩余7张票---<NSThread: 0x60000324a980>{number = 3, name = (null)}
剩余6张票---<NSThread: 0x600003253b80>{number = 5, name = (null)}
剩余5张票---<NSThread: 0x60000324a980>{number = 3, name = (null)}
剩余4张票---<NSThread: 0x600003253b80>{number = 5, name = (null)}
剩余3张票---<NSThread: 0x60000324a980>{number = 3, name = (null)}
剩余2张票---<NSThread: 0x600003253b80>{number = 5, name = (null)}
剩余1张票---<NSThread: 0x60000324a980>{number = 3, name = (null)}
剩余0张票---<NSThread: 0x600003253b80>{number = 5, name = (null)}

2,说明

  • dispatch_semaphore_wait

1>如果信号量的值大于0,就将值-1并往下执行

2>如果信号量的值等于0,就让线程休眠并等待信号

3>收到信号后会唤醒线程并重新判断信号量的值

  • dispatch_semaphore_signal

将信号量的值+1并发送信号

六,dispatch_barrier

1,使用

  • dispatch_barrier_async
- (void)viewDidLoad {
    [super viewDidLoad];
    
    NSLog(@"begin---%@", [NSThread currentThread]);
    __block int data = 99;
    dispatch_queue_t queue = dispatch_queue_create("MyConcurrentQueue", DISPATCH_QUEUE_CONCURRENT);
    
    dispatch_async(queue, ^{
        sleep(1.0);
        NSLog(@"读1---%d---%@", data, [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        sleep(1.0);
        NSLog(@"读2---%d---%@", data, [NSThread currentThread]);
    });
    
    dispatch_barrier_async(queue, ^{
        sleep(1.0);
        data = 100;
        NSLog(@"写---%@", [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        sleep(1.0);
        NSLog(@"读3---%d---%@", data, [NSThread currentThread]);
    });
    
    dispatch_async(queue, ^{
        sleep(1.0);
        NSLog(@"读4---%d---%@", data, [NSThread currentThread]);
    });
    
    NSLog(@"end---%@", [NSThread currentThread]);
}

// 打印
07:23:15.929766+0800 Demo[34756:17878123] begin---<NSThread: 0x60000392ddc0>{number = 1, name = main}
07:23:15.930049+0800 Demo[34756:17878123] end---<NSThread: 0x60000392ddc0>{number = 1, name = main}
07:23:16.932491+0800 Demo[34756:17878203] 读1---99---<NSThread: 0x6000039719c0>{number = 3, name = (null)}
07:23:16.932491+0800 Demo[34756:17878195] 读2---99---<NSThread: 0x60000396a280>{number = 5, name = (null)}
07:23:17.938203+0800 Demo[34756:17878195] 写---<NSThread: 0x60000396a280>{number = 5, name = (null)}
07:23:18.940880+0800 Demo[34756:17878203] 读4---100---<NSThread: 0x6000039719c0>{number = 3, name = (null)}
07:23:18.940883+0800 Demo[34756:17878195] 读3---100---<NSThread: 0x60000396a280>{number = 5, name = (null)}
  • dispatch_barrier_sync
dispatch_barrier_sync(queue, ^{
    sleep(1.0);
    data = 100;
    NSLog(@"写---%@", [NSThread currentThread]);
});

// 打印
07:29:57.114729+0800 Demo[34807:17881597] begin---<NSThread: 0x600003e71580>{number = 1, name = main}
07:29:58.120079+0800 Demo[34807:17881637] 读1---99---<NSThread: 0x600003e4ed00>{number = 4, name = (null)}
07:29:58.120099+0800 Demo[34807:17881639] 读2---99---<NSThread: 0x600003e16a80>{number = 7, name = (null)}
07:29:59.121744+0800 Demo[34807:17881597] 写---<NSThread: 0x600003e71580>{number = 1, name = main}
07:29:59.122171+0800 Demo[34807:17881597] end---<NSThread: 0x600003e71580>{number = 1, name = main}
07:30:00.124470+0800 Demo[34807:17881637] 读4---100---<NSThread: 0x600003e4ed00>{number = 4, name = (null)}
07:30:00.124501+0800 Demo[34807:17881639] 读3---100---<NSThread: 0x600003e16a80>{number = 7, name = (null)}
  • dispatch_barrier_async_and_wait
dispatch_barrier_async_and_wait(queue, ^{
    sleep(1.0);
    data = 100;
    NSLog(@"写---%@", [NSThread currentThread]);
});

// 打印
10:20:10.414882+0800 Demo[35536:17905685] begin---<NSThread: 0x60000084ccc0>{number = 1, name = main}
10:20:11.421195+0800 Demo[35536:17905758] 读2---99---<NSThread: 0x60000080d0c0>{number = 4, name = (null)}
10:20:11.421212+0800 Demo[35536:17905764] 读1---99---<NSThread: 0x600000813100>{number = 3, name = (null)}
10:20:12.422726+0800 Demo[35536:17905685] 写---<NSThread: 0x60000084ccc0>{number = 1, name = main}
10:20:12.423153+0800 Demo[35536:17905685] end---<NSThread: 0x60000084ccc0>{number = 1, name = main}
10:20:13.423888+0800 Demo[35536:17905758] 读4---100---<NSThread: 0x60000080d0c0>{number = 4, name = (null)}
10:20:13.423965+0800 Demo[35536:17905764] 读3---100---<NSThread: 0x600000813100>{number = 3, name = (null)}

2,说明

  • 执行顺序

1>先同时执行在dispatch_barrier函数前添加的任务

2>再单独执行在dispatch_barrier函数中添加的任务

3>然后同时执行在dispatch_barrier函数后添加的任务

  • 对比

1>dispatch_barrier_async:异步执行

2>dispatch_barrier_sync:同步执行,会阻塞当前线程

3>dispatch_barrier_async_and_wait:同步执行,会阻塞当前线程

  • 不能用全局并发队列,否则dispatch_barrier会失效

  • 它常应用于“多读单写”的场景

相关文章

  • iOS多线程:『GCD』详尽总结

    iOS多线程:『GCD』详尽总结 iOS多线程:『GCD』详尽总结

  • iOS多线程(一)-GCD

    iOS多线程-GCD

  • iOS多线程相关面试题

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • 多线程之--NSOperation

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • iOS多线程之--NSThread

    iOS多线程demo iOS多线程之--NSThread iOS多线程之--GCD详解 iOS多线程之--NSOp...

  • [iOS 多线程] iOS多线程-GCD

    iOS多线程-GCD GCD的简介 GCD,全称为 Grand Central Dispatch ,是iOS用来管...

  • iOS 多线程

    iOS 多线程有几种方式 GCD NSOpeartion NSThread phread 多线程 GCD disp...

  • GCD练习

    GCD练习 ios 多线程 GCD : ios 多线程 全剧队列,异步执行 线程间通信 信号量 文件锁 单利模式 ...

  • iOS开发多线程之GCD

    iOS开发多线程之GCDiOS开发之GCD同步任务加强iOS开发之GCD串行队列iOS开发之GCD并发队列 GCD...

  • iOS 多线程

    参考链接 iOS多线程iOS 多线程:『GCD』详尽总结iOS简单优雅的实现复杂情况下的串行需求(各种锁、GCD ...

网友评论

    本文标题:iOS多线程:GCD进阶篇

    本文链接:https://www.haomeiwen.com/subject/kbpbphtx.html