ios swift多線程的實現 Multithreading

1、多線程的概念

Multithreading多線程是指從軟件或硬件上,實現多個線程併發執行的技術。使得能夠同步完成多項任務,提高資源使用效率。


1.1 任務、進程和線程

  • 任務Task:應用程序完成的一個活動,一個task既可以是一個進程,也可以是一個線程;
  • 進程Process:系統進行資源分配和調度的一個獨立單位,在內存中有完備的數據空間和代碼空間;
  • 線程Thread:進程中的一個實體,CPU調度和分派的基本單位;


1.2 線程的Stack space

系統中每一個進程都有自己的內存空間,同個進程中多個線程共用進程的內存空間。

  • 在Mac OS中,主線程的棧空間爲8MB;
  • 在Ios中,主線程的棧空間爲1MB;

應用程序子線程默認棧空間大小爲512KB,子線程允許分配的最小棧空間爲16KB,並且必須是4KB的整數倍。開發者可以通過NSThread線程對象的stacksize來修改一個子線程的棧空間。

let thread = Thread.init(target: self, selector:(VIewController.threadAction),object:nil)
thread.stackSize = 1024*1024


1.3 線程的優先級

  • threadPriority: 0.0-1.0
  • 系統默認優先級是0.5
  • 高優先級並不是100%比低優先級先執行,只是得到CPU調度的紀律更高
1.4 線程的生命週期
  • 創建 對線程對象進行初始化;
  • 就緒 添加到線程池,等待CPU的調度;
  • 運行 是線程處於執行狀態;
  • 阻塞 可以是線程休眠至指定的時間點,或者通過Lock給線程加鎖;
  • 消亡 執行完畢之後自動處於正常消亡狀態;
2、三種常用的多線程技術
2.1 Thread

var imageView = UIImageView()
    var label = UILabel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        imageView = UIImageView(frame: CGRect(x: 0, y: 120, width: 320, height: 300))
        self.view.addSubview(imageView)
        
        label = UILabel(frame: CGRect(x: 0, y: 120, width: 320, height: 300))
        label.backgroundColor = UIColor.lightGray
        label.textAlignment = .center
        label.text = "Loading..."
        label.font = UIFont.systemFont(ofSize: 42)
        self.view.addSubview(label)
        
        let imageUrl = "http://images.apple.com/v/iphone/home/s/home/images/trade_in_iphone_large_2x.jpg"
        let thread = Thread(target: self, selector: #selector(ViewController.downloadImage), object: imageUrl)
        thread.start()
    }
    
    func downloadImage(path : String){
        let url = URL(string: path)
        var data : Data!
        do{
            try data = Data(contentsOf: url!)
            let image = UIImage(data: data)
            self.perform(#selector(ViewController.showImage), on: Thread.main, with: image, waitUntilDone: true)
        }catch{
            print("下載圖片失敗。")
        }
    }
    
    func showImage(image : UIImage){
        self.imageView.image = image
        self.label.isHidden = true
    }



2.2 Operation
override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        topImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 320, height: 280))
        self.view.addSubview(topImageView)
        
        footImageView = UIImageView(frame: CGRect(x: 0, y: 280, width: 320, height: 290))
        self.view.addSubview(footImageView)
        
        let downloadA = getOperation(name: "下載線程A", imageUrl: "http://images.apple.com/v/watch/k/images/overview/watch_03_large.jpg", isTopOne: true)
        
        let downloadB = getOperation(name: "下載線程B", imageUrl: "http://images.apple.com/v/watch/k/images/overview/watch_05_large.jpg", isTopOne: false)
        
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
        queue.addOperation(downloadA)
        queue.addOperation(downloadB)
        
        for operation in queue.operations{
            print("Operation名稱:"+operation.name!)
        }
    }
    
    func getOperation(name : String, imageUrl : String, isTopOne : Bool) -> BlockOperation{
        let download = BlockOperation(block: {_ in
            let url = URL(string: imageUrl)
            var data : Data!
            do{
                Thread.sleep(forTimeInterval: 1.0)
                try data = Data(contentsOf: url!)
                let image = UIImage(data: data)
                if isTopOne{
                    self.perform(#selector(ViewController.showTopImage), on: Thread.main, with: image, waitUntilDone: true)
                }
                else{
                    self.perform(#selector(ViewController.showFootImage), on: Thread.main, with: image, waitUntilDone: true)
                }
            }catch{
                print("下載圖片失敗。")
            }
        })
        download.name = name
        return download
    }
    
    func showTopImage(image : UIImage){
        self.topImageView.image = image
    }
    
    func showFootImage(image : UIImage){
        self.footImageView.image = image
    }




2.3 Grand Central Dispatch
override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        label.frame = CGRect(x: 0, y: 0, width: 320, height: 568)
        label.text = "Loading..."
        label.font = UIFont(name: "Arial", size: 24)
        label.backgroundColor = UIColor.orange
        label.numberOfLines = 0
        label.lineBreakMode = NSLineBreakMode.byWordWrapping
        self.view.addSubview(label)
        
        let apiURL = URL(string: "http://ip.taobao.com/service/getIpInfo.php?ip=27.156.152.57")
        
        let globalQueue = DispatchQueue.global()
        globalQueue.async{
            let result = try? Data(contentsOf: apiURL!)
            let message  = String(data: result!, encoding: String.Encoding.utf8)
            DispatchQueue.main.async
            {
                self.label.text = message
            }
        }
    }

3、總結
  • Thread(基於thread):每個Thread對象對應一個線程,優點是量級較輕,使用簡單,缺點是需要開發者自行管理線程的生命週期、線程同步、加鎖解鎖、睡眠以及喚醒等操作。
  • Operation(基於queue):不需要關心線程的管理和線程同步的事情,可以把精力放在自己需要執行的業務邏輯上,缺點是隻能實現它或者使用它定義好的子類。
  • Grand Central Dispatch(task):基於C語言的一種高效、強大的多核編輯解決方案,其在後端管理着一個線程池,它不僅僅決定代碼塊將在那個線程被執行,還可以根據可用的系統資源對這些線程進行管理。
開發者在多線程技術進行選擇時,如果追求簡便、安全,可以選擇基於隊列的Operation技術。如果需要處理大量併發數據,同時又追求應用程序的性能和效率,可以選擇Grand Central Dispatch。









發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章