zoukankan      html  css  js  c++  java
  • Swift5.x 多线程编程

    //
    //  ViewController16.swift
    //  swiftT
    //
    //  Created by wjwdive on 2020/6/1.
    //  Copyright © 2020 wjwdive. All rights reserved.
    //
    
    import UIKit
    
    class ViewController16: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            
            //1 Thread多线程打印1到10
            for i in 0..<10 {
                Thread.detachNewThread{
                     print(i)//打印是无序的,
                }
            }
            
            //2
            let obj = ObjectThread()
            obj.threadTest()//
            
            //Operation queue  创建队列并添加操作
            let operation = ObjectForThread()
            operation.threadTest()
            
            //completionBlock
            let operationCallBack = ObjectCompletionBlock()
            operationCallBack.threadTest()
            
            //qos 服务质量
            //DispatchQoS.default
            //DispatchQoS.background
            //DispatchQoS.userInitiated
            //DispatchQoS.userInteractive
            //DispatchQoS.unspecified
    
            //
            
            // 创建一个队列
            let queue = DispatchQueue(label: "myQueue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency:
                DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
            //同步执行一个任务
            queue.sync {
                print("in queu sync")
            }
            
            print("after invoke queue method!")
            //延迟执行
            queue.asyncAfter(deadline: .now() + 2) {
                print("asyncAfter")
            }
            //系统时钟 关机之后系统时钟会暂停
            //DispatchTime
            //墙上挂的时钟   ,不关注系统时间,关机计算时间
            //DispatchWallTime
            
            
            let group = DispatchGroup()
            group.enter()
            queue.async {
                sleep(1)
                print("模拟接口1请求完成")
                group.leave()
            }
            
            group.enter()
            queue.async {
                sleep(1)
                print("模拟接口2请求完成")
                group.leave()
            }
            
            print("group enter() 调用完成")
            group.wait()//阻塞
            print("接口1,接口2调用完成")
            
            let groupn = DispatchGroup()
            groupn.enter()
            print("groupn enter() 调用完成")
    
            queue.async {
                sleep(1)
                print("模拟接口A请求完成")
                groupn.leave()
            }
            
            groupn.enter()
            queue.async {
                sleep(1)
                print("模拟接口B请求完成")
                groupn.leave()
            }
            
            print("groupn enter() 调用完成")
            groupn.notify(queue: queue) {
                print("接口A,接口B调用完成")
            }
            print("验证不阻塞")
            
            
            //timer
            var seconds = 10
            let timer = DispatchSource.makeTimerSource(flags: [], queue: queue)
            timer.schedule(deadline: .now(), repeating: 1.0)
            timer.setEventHandler {
                seconds -= 1
                if seconds < 0 {
                    timer.cancel()
                } else {
                    print(seconds)
                }
            }
            timer.resume()
            
            
            //串行队列里先同步调一个任务,任务里再同步调一个任务
            lockSimple()
            
        }
    
    }
    
    // Swift多线程编程
    // 思考: 为什么几乎所有的GUI框架都是单线程的?
    // 回答: 因为多线程会有线程调度,虽然多线程会加快运算速度,但是带来的线程调度和开销,资源抢占,死锁等问题会更多。
    
    // 多线程编程方式:【3种】
    // Thread, Cocoa Operation, GCD(Grand Central Dispatch)
    
    // Thread,在三种多线程技术中是最轻量级的,但需要自己管理自己线程的生命周期和线程同步。线程同步对数据的枷锁会有一定的系统开销。
    
    //
    //Thread 创建线程的3种方式
    func ThreadCreat1() {
        //1
        Thread.detachNewThread {
            print("detachNewThread creat a therad!")
        }
        
    }
    //
    //func ThreadCreat2() {
    //    //2
    //    Thread.detachNewThreadSelector(#seletor(funcName): Selector, toTarget: self, with: nil) {
    //
    //    }
    //
    //}
    //
    //func ThreadCreat3() {
    //    //3
    //    Thread(target: self., selector: #seletor(funcName), object: nil)
    //}
    
    
    
    //在Playground里需要添加以下声明
    //import PlaygroundSupport
    //PlaygroundPage.current.needIndefiniteExecution = true
    class ObjectThread {
        func threadTest() {
            let thread = Thread(target: self, selector: #selector(threadWorker), object: nil)
            //使用初始化器创建的线程需要手动start
            thread.start()
        }
        
        @objc func threadWorker() {
            print("threadWorker")
        }
    }
    
    //面向对象
    // Operation + OperationQueue
    //取消,依赖,任务优先级,复杂逻辑,保存业务子状态,子类化
    //Operation, Block Operation
    
    //Operation四个状态(属性)
    //1、 isReady
    //2、 isExecuting
    //3、 isFinished
    //4、 isCancelled
    
    // OPeration 运行方式
    // 1、sync 同步 在main方法里
    // 2、async 异步,比如网络请求
    // start()
    // isAsynchromous
    // isExecuting
    // isFinished
    
    // Operation可以添加依赖
    //func addDependency(Operation)
    //func removeDependency(Operation)
    //var dependencied:[Operation]
    
    //Operation Queue
    //1、Operation Queue队列里可以加入很多个Operation, 可以把OperationQueue看作一个线程池,可以往线程池中添加操作(Operation)到队列中
    //2、底层使用GCD
    //3、maxConcurrentOperationCount 可以设置最大并发数【最多不超过5个】
    //4、defaultMaxConcurrentOperationCount 根据当前系统条件动态确定的最大并发数
    //5、可以取消所有Operation,但是当前正在执行的不会执行
    //6、所有Operation执行完毕后退出销毁
    
    //BlockOperation
    class ObjectForThread {
        func threadTest() {
            let operation = MyOperation()
            let queue = OperationQueue()
            queue.addOperation(operation)
        }
    }
    
    class MyOperation: Operation {
        override func main() {
            sleep(1)
            print("MyOperation")
        }
    }
    
    
    //completionBlock  带回调的OperationBlock
    class ObjectCompletionBlock {
        func threadTest() {
            let operation = MyOperation()
            operation.completionBlock = {()-> Void in
                print("---operation.completionBlock---")}
            let queue = OperationQueue()
            queue.addOperation(operation)
            print("threadTest")
        }
    }
    
    
    
    //封装一个网络请求
    
    //GCD
    // 任务+队列
    // 易用,高效,性能
    //功能:
    //1、创建管理Queue
    //2、提交Job
    //3、Dispatch Group
    //4、管理Dispatch Object
    //5、信号量 Semaphore
    //6、队列屏障
    //7、Dispatch Source
    //8、Queue Context 数据
    //9、Dispatch I/O Channel
    //10、Dispatch Data 对象
    
    
    // Dispatch.main
    // Dispatch.global
    // DispatchQueue(label:,qos:,autoreleaseFrequency:,target:) //自己创建
    // queue.label
    // setTarget(queue: DispatchQueue?)
    
    //GCD-队列
    //1、最终的目标队列都是主队列和全局队列
    //2、如果把一个并行队列的目标队列设置成一个串行队列,那么并行队列将不再并行
    //3、如果多个队列的目标队列都设置为同一个串行队列,那么这多个队列连同b目标队列里的任务都将穿行执行
    //4、如果设置目标队列形成了环,结果是不可预期的
    //5、如果在一个队列正在执行任务的时候更换目标队列,结果也是不可预期的
    
    //GCD里的基本操作
    //1、穿行和并行:描述任务之间是如何运行的,串行任务每次仅仅执行一个,并行任务可以多个同时执行
    //2、同步和异步: 描述的是任务的执行过程是如何的。同步:同一个过程会等待
    
    //同步任务
    // A
    // |
    // B
    // |
    // A
    
    //异步任务
    // A
    // |
    // A B
    // | /
    // A
    
    // GCD-sync
    // 提交任务到当前队列里,并且直到任务执行完成,当前队列才返回
    //func sync(execute: DispatchWorkItem)
    // submints a work for execution on the current queue and returns after that block finished executing
    
    //func sync(execute: () -> Void)
    // submits a block object for executino and returns after that block finishes executing
    
    //func sync<T>(execute: () -> T) -> T
    //submits a work item for execution and returns the results from that item after it finishes executing
    //func sync<T>(flags: DispatchWorkItemFlags, execute:() -> T) -> T
    //Submits a work item for execution using the specified attributes and returns the results from that item after it finishes executing
    
    //GCD-async
    //调度一个任务立即取执行,但是不用等待任务执行完当前队列就会返回
    
    //GCD-asyncAfter
    //调度一个任务多久之后执行,但是不用等待任务执行完当前队列就会返回
    //DispatchQueue.main.asyncAfter(deadline: .now() + 2.5) {
        //执行的任务
        //print("延迟2.5s执行的任务")
    //}
    
    
    //DispatchGroup
    //enter和leave必须成对出现
    //DispatchGroup-wait
    
    
    //DispatchGroup-notify
    
    //DispatchSource
    //dispatch source是一个监视某些类型事件的对象。这些事件发生时,它自动将一个task放入一个dispatch queue的执行历程中
    //用途
    //Mach port send right state changes
    //Mach port receive right state changes
    //External process state change
    //File descriptor ready for read
    //File descriptor ready for write
    //Filesystem node event.
    //PPSIX signal
    //custom timer
    //custom event
    
    // GCD源码剖析
    
    
    // 一个死锁的例子
    //串行队列,异步执行一个任务,任务里又串行执行一个任务
    func lockSimple() {
        let queue = DispatchQueue(label: "serial queue")
        queue.async {
            print("in queue async")
            queue.async {
                print("in queue sync")
            }
        }
    }
    
    //并行队列不会死锁了
    func noLockSimple() {
        let queueCuncurrent = DispatchQueue(label: "concurrent queue", qos: DispatchQoS.default, attributes: DispatchQueue.Attributes.concurrent, autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency.inherit, target: nil)
        queueCuncurrent.async {
            print("in queueCuncurrent async")
            queueCuncurrent.sync {
                print("in queueCuncurrent sync")
            }
        }
    }
    
    
    // 死锁
    //资源竞争或彼此通讯 死锁时指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞现象,若无外力作用,它们都将无法推进下去。此时系统处于死锁状态或者系统产生了死锁。
    //例子:线程A 在t1时刻占用了X资源(t1开始A锁定了X),线程B在t2时刻占用了Y资源(t2开始B锁的了Y),t3时刻线程A又要尝试占用Y资源(资源Y t2 时刻就已被线程B锁的),t4时刻线程B又要尝试占用资源X,这时候就会发生死锁。AB线程都个字拥有了资源但又需要对方已经在使用的资源,就会发生死锁。(时刻:t1 < t2 < t3 < t4)
    // 临界区:
    // 就是一段代码不能被并发执行,也就是,两个线程不能同时执行这段代码
    
    // 静态条件
    // 两个或多个线程读写某些共享数据,而最后的结果取决于线程运行时的精确时序。
    // 例如多个线程读写银行存款余额,
    
    // 优先级反转
    // 假如有高A-中B-低C3种优先级的线程,t1时刻低优先级的线程C先获取了一个共享资源S,t2时刻,高优先级的线程A尝试获取资源S,但是线程C还在占用资源S,所以A就被挂起了。这时B就有了最高可运行优先级,B在C运行完之后会立即获取到资源S(A还在挂起状态),B继续占有S运行,期间A会继续间隔尝试获取S,但是得不到S无法运行,期间C若要再次运行,他没有B的优先级高也得不到S.所以优先级排序ABC变成了BAC。也就是优先级反转
    
    // 并发与并行
    // 并行:一般发生在CPU多核多线程的情况下,两个线程在两个核上同时执行,而不需要间断
    // 并发:需要有一些对线程持续的调度,或者有一些上下文的切换,来看起来像Thread1,Thread2同时执行。但实际上是两个线程相互让渡系统执行事件来达到的一种假象。
    //
    //iOS中的锁
    //1 OSSpinLock
    //2 dispatch_semaphore
    //3 pthread_mutex
    //4 NSLock
    //5 NSCondation
    //6 pthread_mutex(recursive)
    //7 NSRecursiveLock
    //8 NSConditionLock
    //9 @synchronized swift 中已被移除
    
    //1 OSSpinLock:
    // 线程是通过busy-wait-loop的方式来获得锁的,任意时刻只有一个线程能获得锁,其他线程忙等待直到获得锁。
    // 普通的锁实现,当一个线程加锁一个资源时,其他的线程再访问资源,就获取不到,就会进入休眠状态,直到活跃线程执行完,等待系统把休眠的线程唤醒,才能给资源加上锁继续执行。但是OSSpinLock任意时刻只有一个线程能获得锁,其他线程忙等待直到获得锁。
    
    //bool lock = false //一开始没有锁上,任何线程都可以申请锁
    //do {
    //    while(test_and_set(&lock));//test_and_set是一个原子操作
    //        Critical section;//临界区
    //    lock = fasle // 相当于释放锁,这样别的线程可以进入临界区
    //        Reminder section //不需要锁保护代码
    //}
    
    //OSSpinLock适用范围
    //1、临界区尽量简短,控制在100行代码以内,不要有显式或隐式的系统调用,调用的函数也尽量简短。因为执行临界区代码非常长的话,所有的等待进入临界区的线程访问临界区的时候都会忙等待,造成资源浪费或卡顿。
    //2、保证访问锁的线程全部都处于同一优先级。可能会出现优先级反转
    
    //9 @synchronized swift 中已被移除,不过可以自己实现该功能
    func synchorized(_ obj: AnyObject, closure: () -> () ) {
        objc_sync_enter(obj)
        closure()
        objc_sync_exit(obj)
    }
    //注意点:
    //1 只有传同样的synchronized,才能起到加锁作用
    //2 如果传 nil ,无法起到加锁的作用
    //3 可以重用
    //4 synchorized不会持有传给他的对象
    
    //objc_sync_enter的实现
    //int objc_sync_enter(id obj) {
    //    int result = OBJC_SYNC_SUCCESS;
    //    if(obj) {
    //        SyncData *data = id2data(obj, ACQUIRE);
    //        assert(data);
    //        data->mutex.lock();
    //    }else {
    //        //@synchorized(nil) does nothing
    //        if(DebugNilSync) {
    //            _objc_inform("NIL SYNC DEBUG: @synchorized(nil);set a breadpoint on 0bjc_sync_nil to debug")
    //        }
    //        objc_sync_nil();
    //
    //    }
    //    return result;
    //}
    
    // end synchorizing on 'obj'.
    // Returns OBJC_SYNC_SUCCESS or OBJC_SYNC_NOT_OWNING_THREAD_ERROR
    //int objc_sync_exit(id obj){
    //    int reuslt = OBJC_SYNC_SUCCESS;
    //    if(obj) {
    //        SyncData *data = id2data(obj, RELEASE);
    //        if(!data) {
    //            result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
    //        }else {
    //            bool okay = data->mutex.tryUnlock();
    //            if(!okay) {
    //                result = OBJC_SYNC_NOT_OWNING_THREAD_ERROR;
    //            }
    //        }
    //    } else {
    //        // @synchorized(nil) does nothing
    //    }
    //    return result
    //}
    
    // SyncData是什么?
    //1 SyncData是可以重用的,(threadCount == 0)
    //2 存在全局的map里
    
    //SyncData 定义
    //typedef struct alignas(ChaheLineSize) SyncData {
    //    struct SyncData* nextData;
    //    DisguisedPtr<ojc_object> object;
    //    int32_t threadCount;
    //    recursive_mutext_t mutex;
    //} SyncData;
    
    //如果是对 ObjcA, ObjcB, ObjcC 三个对象进行synchorized操作,其中先对ObjcA操作两次。
    
    //多线程的应用场景
    //1 一个也没有三个网络请求,需要再三个网络请求返回的时候刷新界面
    //2 需要一个线程安全的Array的读写
    //3 编写一个多线程下载器,可执行多个下载任务,每个任务可以保存当下下载的字节数,总字节数,可以设置回调得到当前下载进度
    //4 需要再主线程等待一个异步任务返回,才能继续执行下面的逻辑,但是又不希望堵塞用户事件。
    
    
    
  • 相关阅读:
    bzoj 4974 [Lydsy1708月赛]字符串大师 KMP 最小循环元 构造
    4.10 省选模拟赛 约数 数论 转换 三元组个数
    loj #6039 「雅礼集训 2017 Day5」珠宝 分组背包 决策单调性优化
    CF R 632 div2 1333F Kate and imperfection
    CF R 632 div2 1333D Challenges in school №41
    luogu P3703 [SDOI2017]树点涂色
    3.28 省选模拟赛 染色 LCT+线段树
    luogu P3279 [SCOI2013]密码
    4.8 省选模拟赛 相遇 求树上两路径交
    Hyper-V 2016 上安装windows7激活重启后黑屏无法进入系统
  • 原文地址:https://www.cnblogs.com/wjw-blog/p/13038553.html
Copyright © 2011-2022 走看看