swift多线程GCD常用需求总结及工具代码

先把常用的Demo放前面,方便查找,能满足绝大多数需求,各种理论后续再补充在后面。

1.延迟执行

        let delay = DispatchTime.now() + .seconds(10)//计算需要开始的时间
        print("添加了一个10秒后执行的任务")
        DispatchQueue.main.asyncAfter(deadline: delay) {
            //此处写入延迟执行的代码
            print("延迟执行代码已开始执行")
            sleep(3)
            print("延迟执行代码已执行完毕")
        }

2.线程安全锁(某一时刻只能一个线程调用)

swift3中废弃了dispatch_once,这里列举了可用的线程锁,两种方式如下

    let lock = NSLock.init()
    
    func funcMultiThread() {
        //加锁方式1,类似于oc中@synchronized,互斥锁的一种
        objc_sync_enter(self)
        print("this is multi thread func begin")
        sleep(4)
        print("this is multi thread func ended")
        objc_sync_exit(self)
        
        //加锁方式2
        lock.lock()
        print("this is multi thread func begin")
        sleep(4)
        print("this is multi thread func ended")
        lock.unlock()
    }

3.单次执行(单例)

swift3中废弃了dispatch_once,GCD中没有只执行一次的方法,只能自己生成全局静态变量自己控制只调用一次。

但对于单例,可以用如下方式替换,相比OC中实现方式:

+ (instancetype)sharedInstance {
    static LAPayRedPointManager *_sharedInstance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _sharedInstance = [[self alloc] init];
    });
    return _sharedInstance;
}

Swift中要简单得多,Demo如下

class FrankObj: NSObject {
    static let shared = FrankObj()
    var name:String = "frank"
}
使用上述方法,直接调用FrankObj.shared即可生成FrankObj的单例。经本人详细测试,该写法线程安全,多次创建只返回同一地址对象。

4.开辟子线程,完成后通知主线程

    /// 异步执行,完毕通知主线程
    func func1() {
        print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
        
        DispatchQueue.global().async {
            print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")
            sleep(3)
            print("异步代码执行完毕,准备通知主线程")
            
            DispatchQueue.main.sync {
                print("通知主线程啦")
                print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
            }
        }
    }

5.三个任务,分别开辟三个线程,都执行完毕后通知主线程(group)

  @IBAction func ibaButton1(_ sender: Any) {
        
        print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")

        let group = DispatchGroup();
        
        let queue1 = DispatchQueue(label: "queue1")
        queue1.async(group:group){
            print("queue1开始任务")
            print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")
            sleep(4)
            print("queue1结束任务")
        }
        
        let queue2 = DispatchQueue(label: "queue2")
        queue2.async(group:group){
            print("queue2开始任务")
            print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")
            sleep(8)
            print("queue2结束任务")
        }
        
        let queue3 = DispatchQueue(label: "queue3")
        queue3.async(group:group){
            print("queue3开始任务")
            print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")
            sleep(30)
            print("queue3结束任务")
        }
        
        group.notify(queue: DispatchQueue.main) {
            print("上述任务都完成,通知主线程")
            print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")
        }
    }

打印日志如下

{number = 1, name = main}此执行异步代码,isMainThread == true
queue2开始任务
queue1开始任务
queue3开始任务
在{number = 3, name = (null)}此执行异步代码,isMainThread == false
在{number = 5, name = (null)}此执行异步代码,isMainThread == false
在{number = 4, name = (null)}此执行异步代码,isMainThread == false
queue1结束任务
queue2结束任务
queue3结束任务
上述任务都完成,通知主线程
在{number = 1, name = main}此执行异步代码,isMainThread == true

有时特殊需求,上面三个子线程又有一些异步耗时操作,需要在异步耗时操作完成之后再调用notify,可以对queue1做如下处理

   let queue1 = DispatchQueue(label: "queue1")
        queue1.async(group:group){
            print("queue1开始任务")
            print("在\(Thread.current)此执行异步代码,isMainThread == \(Thread.isMainThread)")
            queue1.async(execute: {
                group.enter()//这行很重要
                print("这是queue1的子线程中的log,子线程开始")
                sleep(6)
                print("这是queue1的子线程中的log,子线程结束")
                group.leave()
            })
            print("queue1结束任务")
        }
这样就能保证queue子线程完成之前不会回调notify,group.leave()之后,才会调用Notify

6.有两个任务,想在任务1执行完毕后,执行任务2。任务比较少,如果利用group有点大材小用,可以用下面这几种方式做线程依赖

        let queue1 = DispatchQueue(label: "queue1")
        // 任务1
        let work1 = DispatchWorkItem {
            print("开始work1")
            sleep(4)
            print("结束work1")
        }
        // 任务2
        let work2 = DispatchWorkItem {
            print("开始work2")
            sleep(2)
            print("结束work2")
        }
        
        // 第一种绑定方式
        work1.notify(queue: queue1) {
            // 执行2
            work2.perform()
        }
        
        // 第二中绑定方式
        work1.notify(queue: queue1, execute: work2)
        
        // 第三种可以直接不创建work2,在闭包中执行work的工作
        work1.notify(queue: queue1) {
            print("开始闭包中 work2")
            sleep(2)
            print("结束闭包中 work2")
        }
        
        // 执行任务:
        // 方式1:任务放在队列中并执行
        queue1.async(execute: work1)
        // 方式2:如果不指定队列,会在当前的队列中执行,如果在主线程中执行会造成线程阻塞
        work1.perform()

7.一共有五个任务,abcde,其中ab并发进行,ab都执行完毕之后执行C,C执行完毕之后DE并发执行。当然,可以用上述Group进行,但GCD中封住了更方便的方法,Barrier,如下

    func func4() {
        let queue = DispatchQueue(label: "queuename", attributes: .concurrent)  //并发队列
        
        queue.async {
            print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
            print("开始第1个任务")
            sleep(4)
            print("结束第1个任务")
        }
        
        queue.async {
            print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
            print("开始第2个任务")
            sleep(1)
            print("结束第2个任务")
        }
        
        queue.sync(flags: .barrier,execute:{
            print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
            print("开始执行Barrier中的任务")
            sleep(9)
            print("barrier中的任务执行完毕")
        })
        
        queue.async {
            print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
            print("开始第3个任务")
            sleep(2)
            print("结束第3个任务")
        }
        
        queue.async {
            print("当前是\(Thread.current),isMainThread == \(Thread.isMainThread)")
            print("开始第4个任务")
            sleep(4)
            print("结束第4个任务")
        }
    }

打印结果如下

当前是{number = 4, name = (null)},isMainThread == false
当前是{number = 3, name = (null)},isMainThread == false
开始第2个任务
开始第1个任务
结束第2个任务
结束第1个任务
当前是{number = 3, name = (null)},isMainThread == false
开始执行Barrier中的任务
barrier中的任务执行完毕
当前是{number = 3, name = (null)},isMainThread == false
当前是{number = 5, name = (null)},isMainThread == false
开始第3个任务
开始第4个任务
结束第3个任务
结束第4个任务

8.工程中有N个任务,依次加入队列。既要控制最大并发数,又要充分利用性能,这时候需要用到信号量的概念。简单说,信号量就是在多线程中设置最大并发数,达到了,其余的线程等待;未达到,从线程队列中找下一个加进来。

swift:

let group = DispatchGroup()
let semaphore = DispatchSemaphore(value: 10)
let queue  = DispatchQueue.global()
for  i in 0 ... 100 {
    //由于是异步执行的,所以每次循环Block里面的dispatch_semaphore_signal根本还没有执行就会执行dispatch_semaphore_wait,从而semaphore-1.当循环10此后,semaphore等于0,则会阻塞线程,直到执行了Block的dispatch_semaphore_signal 才会继续执行
    semaphore.wait()
    queue.async(group: group){
    print("\(i)")
    sleep(3)
    semaphore.signal()
    }
}

OC:

// 创建队列组
dispatch_group_t group = dispatch_group_create();
// 创建信号量,并且设置值为10
dispatch_semaphore_t semaphore = dispatch_semaphore_create(10);
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
for (int i = 0; i < 100; i++) {
    // 由于是异步执行的,所以每次循环Block里面的dispatch_semaphore_signal根本还没有执行就会执行dispatch_semaphore_wait,从而semaphore-1.当循环10此后,semaphore等于0,则会阻塞线程,直到执行了Block的dispatch_semaphore_signal 才会继续执行
    dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
    dispatch_group_async(group, queue, ^{ NSLog(@"%i",i);
        sleep(2);
        // 每次发送信号则semaphore会+1,
        dispatch_semaphore_signal(semaphore);
        });
}

你可能感兴趣的:(iOS,Swift)