วิธีใช้เธรดพื้นหลังอย่างรวดเร็ว


329

วิธีการใช้เธรดอย่างรวดเร็ว

dispatchOnMainThread:^{

    NSLog(@"Block Executed On %s", dispatch_queue_get_label(dispatch_get_current_queue()));

}];

คุณมีปัญหาในการแปลงส่วนใด
nschum

2
ทำไมคุณมี]ก่อนอัฒภาคในบรรทัดสุดท้าย?
akashivskyy

3
มันจะมีประโยชน์ถ้าคุณอธิบายว่าคุณติดอยู่ที่ไหนหรือต้องการความช่วยเหลือ
nsuinteger

4
คุณต้องยอมรับคำตอบที่ถูกต้องหากมันช่วยคุณได้จริง ๆ มันจะช่วยให้คนอื่น ๆ ค้นหาคำตอบที่ถูกต้องด้วย
Amit Singh

DispatchQueue.global(qos: .background).async { print("Run on background thread") DispatchQueue.main.async { print("We finished that.") // only back on the main thread, may you access UI: label.text = "Done." } }
Anurag Sharma

คำตอบ:


708

Swift 3.0+

สิ่งต่างๆมากมายได้รับการปรับปรุงให้ทันสมัยใน Swift 3.0 การใช้งานบางอย่างบนเธรดพื้นหลังมีลักษณะดังนี้:

DispatchQueue.global(qos: .background).async {
    print("This is run on the background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}

Swift 1.2 ถึง 2.3

let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    print("This is run on the background queue")

    dispatch_async(dispatch_get_main_queue(), { () -> Void in
        print("This is run on the main queue, after the previous code in outer block")
    })
})

Pre Swift 1.2 - ปัญหาที่ทราบแล้ว

ในฐานะของ Swift 1.1 Apple ไม่สนับสนุนไวยากรณ์ข้างต้นโดยไม่มีการแก้ไข ผ่านไม่ได้ทำงานจริงใช้แทนQOS_CLASS_BACKGROUNDInt(QOS_CLASS_BACKGROUND.value)

สำหรับข้อมูลเพิ่มเติมโปรดดูเอกสารประกอบของแอปเปิ้ล


23
และถ้ามีคนต้องการให้ Swift ชอบAsync.background {}
ซินแท

ฉันใช้รหัสของคุณใน xCode 6.0.1 และ iOS 8 มันให้ข้อผิดพลาดเป็น "QOS_CLASS_BACKGROUND" ส่งคืนคลาสและเป็นประเภท UInt32 และ "dispatch_get_global_queue" ต้องการพารามิเตอร์ที่ 1 เนื่องจากข้อผิดพลาดในการพิมพ์
Zalak Patel

ดังนั้นใน Xcode 6.1.1 ฉันไม่ได้รับข้อผิดพลาดในการใช้ "QOS_CLASS_BACKGROUND แบบธรรมดา" มันคงที่หรือไม่
Lucas Goossen

@LucasGoossen ใช่มันได้รับการแก้ไขแล้ว ฉันอัปเดตโพสต์ตามนั้น
tobiasdm

1
@NikitaPronchik ไม่ชัดเจนจากคำตอบใช่ไหม อื่น ๆ โปรดแก้ไขมัน
tobiasdm

123

แนวทางปฏิบัติที่ดีที่สุดคือการกำหนดฟังก์ชั่นที่ใช้ซ้ำได้ที่สามารถเข้าถึงได้หลายครั้ง

ฟังก์ชั่นที่นำกลับมาใช้ได้:

เช่นที่อื่นเช่น AppDelegate.swift เป็นฟังก์ชั่นสากล

func backgroundThread(_ delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {
    dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INITIATED.value), 0)) {
        background?()

        let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
        dispatch_after(popTime, dispatch_get_main_queue()) {
            completion?()
        }
    }
}

หมายเหตุ: ใน Swift 2.0 ให้แทนที่QOS_CLASS_USER_INITIATED ค่าด้านบนด้วยQOS_CLASS_USER_INITIATED.rawValueแทน

การใช้:

A. ในการรันโปรเซสในพื้นหลังด้วยความล่าช้า 3 วินาที:

    backgroundThread(3.0, background: {
            // Your background function here
    })

B. ในการเรียกใช้กระบวนการในพื้นหลังให้รันการดำเนินการให้เสร็จสิ้นในโฟร์กราวน์:

    backgroundThread(background: {
            // Your function here to run in the background
    },
    completion: {
            // A function to run in the foreground when the background thread is complete
    })

C. หากต้องการหน่วงเวลา 3 วินาที - โปรดสังเกตการใช้พารามิเตอร์ความสมบูรณ์โดยไม่มีพารามิเตอร์พื้นหลัง:

    backgroundThread(3.0, completion: {
            // Your delayed function here to be run in the foreground
    })

1
ตัวอย่างที่ดีควรเป็นคำตอบที่ถูกต้อง @Dale Clifford
LoVo

วิธี Swift-y ที่ทันสมัยระดับสูงที่ยอดเยี่ยมในการเข้าถึงวิธีการ GCD ที่ล้าสมัยจากไลบรารี C ระดับต่ำ ควรมาเป็นมาตรฐานใน Swift
Craig Grummitt

2
ดีมาก. คุณช่วยยืนยันได้ไหมว่าความล่าช้านั้นใช้ได้กับบล็อกที่เสร็จสมบูรณ์เท่านั้น นั่นหมายความว่าการหน่วงเวลาใน A. ไม่มีผลกระทบและบล็อกพื้นหลังจะดำเนินการทันทีโดยไม่ล่าช้า
ObjectiveTC

1
คุณควรจะสามารถแทนที่if(background != nil){ background!(); }ด้วยbackground?()ไวยากรณ์ที่ค่อนข้างรวดเร็ว?
Simon Bengtsson

1
คุณช่วยอัพเดทสิ่งนี้สำหรับ Swift 3 ได้ไหม แปลงอัตโนมัติกลายเป็นแต่โยนข้อผิดพลาดเหมือนDispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async { cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'วิธีแก้ปัญหาการทำงานอยู่ที่นี่ ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async)
Dev-iL

111

คำตอบของ Dan Beaulieu ใน swift5 (ทำงานตั้งแต่ swift 3.0.1 ด้วย)

สวิฟท์ 5.0.1

extension DispatchQueue {

    static func background(delay: Double = 0.0, background: (()->Void)? = nil, completion: (() -> Void)? = nil) {
        DispatchQueue.global(qos: .background).async {
            background?()
            if let completion = completion {
                DispatchQueue.main.asyncAfter(deadline: .now() + delay, execute: {
                    completion()
                })
            }
        }
    }

}

การใช้

DispatchQueue.background(delay: 3.0, background: {
    // do something in background
}, completion: {
    // when background job finishes, wait 3 seconds and do something in main thread
})

DispatchQueue.background(background: {
    // do something in background
}, completion:{
    // when background job finished, do something in main thread
})

DispatchQueue.background(delay: 3.0, completion:{
    // do something in main thread after 3 seconds
})

น่าทึ่งขอบคุณที่อัปเดตเป็นอย่างดีในรูปแบบ Swift 3.0.1!
Dale Clifford

1
ฉันใช้ส่วนขยายมากกว่าคนที่มีชีวิต แต่มีอันตรายที่แท้จริงในการใช้ส่วนขยายที่ไม่ต่างจากต้นฉบับเลย!
Fattie

@Frouo สง่างามมากเป็นไปได้หรือไม่ที่จะเพิ่มตัวจัดการที่สมบูรณ์เมื่อ 4 async เรียกทั้งหมดว่าเสร็จสิ้น? ฉันรู้ว่ามันเป็นเรื่องเล็กน้อย
eonist

1
ใช่ลืมลิงค์นั้น สิ่งที่คุณต้องมีก็คือกลุ่มส่ง - มันง่ายมาก ๆ ไม่ต้องกังวลเลย!
Fattie

1
@DilipJangid คุณทำไม่ได้ยกเว้นว่างานของคุณในการbackgroundปิดจะยาวมากมาก (~ = ไม่มีที่สิ้นสุด) วิธีนี้ทำขึ้นมาเป็นระยะเวลาที่ จำกัด : เวลาที่งานแบ็คกราวด์ของคุณต้องดำเนินการ ดังนั้นcompletionการปิดจะถูกเรียกใช้ทันทีที่เวลาในการประมวลผลงานพื้นหลังของคุณ + ความล่าช้าได้ผ่านไป
2560

42

รุ่น Swift 3

Swift 3 ใช้DispatchQueueคลาสใหม่เพื่อจัดการคิวและเธรด ในการรันบางสิ่งบนเธรดพื้นหลังคุณจะต้องใช้:

let backgroundQueue = DispatchQueue(label: "com.app.queue", qos: .background)
backgroundQueue.async {
    print("Run on background thread")
}

หรือถ้าคุณต้องการบางสิ่งในโค้ดสองบรรทัด:

DispatchQueue.global(qos: .background).async {
    print("Run on background thread")

    DispatchQueue.main.async {
        print("We finished that.")
        // only back on the main thread, may you access UI:
        label.text = "Done."
    }
}

คุณสามารถรับข้อมูลเชิงลึกเกี่ยวกับ GDC ใน Swift 3 ได้จากบทแนะนำนี้


กล่าว. เนื่องจากคำตอบของคุณดีที่สุดฉันจึงโยนรหัสเพื่อแสดงว่าคุณ "โทรกลับเมื่อเสร็จสิ้น" รู้สึกอิสระที่จะผ่อนคลายหรือแก้ไขไชโย
Fattie

35

จากบทช่วยสอนของ Jameson Quave

สวิฟท์ 2

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), {
    //All stuff here
})

3
เพื่อความกระจ่างแจ้งเหตุใดจึงใช้สิ่งนี้แทนคำตอบที่ยอมรับได้? นี่เป็นเพียง API ที่เก่ากว่าหรือเปล่า
ไซเรน

1
@ ไซเรนฉันคิดว่ามันจะมีประโยชน์มากสำหรับแอพที่สนับสนุน <iOS 8
bperdue

ฉันใช้สิ่งนี้สำหรับ iOs 8.2 เพื่อบังคับกระบวนการ
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT แปลงกลับเป็น QOS_CLASS_DEFAULT ดังนั้นฉันคิดว่าคุณสามารถพูดได้ว่ามันเป็นไวยากรณ์ระดับสูงขึ้น / ได้รับการยอมรับ
PostCodeism

34

ใน Swift 4.2 และ Xcode 10.1

เรามีคิวสามประเภท:

1. คิวหลัก: คิวหลักคือคิวอนุกรมที่สร้างขึ้นโดยระบบและเชื่อมโยงกับเธรดหลักของแอปพลิเคชัน

2. Global Queue: Global คิวเป็นคิวที่เกิดขึ้นพร้อมกันซึ่งเราสามารถร้องขอตามลำดับความสำคัญของงาน

3. คิวที่กำหนดเอง: ผู้ใช้สามารถสร้างได้ คิวที่เกิดขึ้นพร้อมกันที่กำหนดเองจะถูกแมปเข้ากับหนึ่งในคิวทั่วโลกโดยการระบุคุณสมบัติคุณภาพการบริการ (QoS)

DispatchQueue.main//Main thread
DispatchQueue.global(qos: .userInitiated)// High Priority
DispatchQueue.global(qos: .userInteractive)//High Priority (Little Higher than userInitiated)
DispatchQueue.global(qos: .background)//Lowest Priority
DispatchQueue.global(qos: .default)//Normal Priority (after High but before Low)
DispatchQueue.global(qos: .utility)//Low Priority
DispatchQueue.global(qos: .unspecified)//Absence of Quality

คิวทั้งหมดเหล่านี้สามารถดำเนินการได้สองวิธี

1. การดำเนินการแบบซิงโครนัส

2. การดำเนินการแบบอะซิงโครนัส

DispatchQueue.global(qos: .background).async {
    // do your job here
    DispatchQueue.main.async {
        // update ui here
    }
}

//Perform some task and update UI immediately.
DispatchQueue.global(qos: .userInitiated).async {  
    // Perform task
    DispatchQueue.main.async {  
        // Update UI
        self.tableView.reloadData()  
    }
}

//To call or execute function after some time
DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
    //Here call your function
}

//If you want to do changes in UI use this
DispatchQueue.main.async(execute: {
    //Update UI
    self.tableView.reloadData()
})

จาก AppCoda: https://www.appcoda.com/grand-central-dispatch/

//This will print synchronously means, it will print 1-9 & 100-109
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.sync {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}

//This will print asynchronously 
func simpleQueues() {
    let queue = DispatchQueue(label: "com.appcoda.myqueue")

    queue.async {
        for i in 0..<10 {
            print("🔴", i)
        }
    }

    for i in 100..<110 {
        print("Ⓜ️", i)
    }
}

1
บทแนะนำที่ดีที่สุดสำหรับหัวข้อmedium.com/@gabriel_lewis/ …
iOS

ฉันไม่เห็นการเปลี่ยนแปลงใด ๆ เมื่อคุณใช้.background QoS หรือ.userInitiatedแต่สำหรับฉันมันได้ผลด้วย.background
สนิม

24

Swift 4.x

ใส่ไว้ในไฟล์บางไฟล์:

func background(work: @escaping () -> ()) {
    DispatchQueue.global(qos: .userInitiated).async {
        work()
    }
}

func main(work: @escaping () -> ()) {
    DispatchQueue.main.async {
        work()
    }
}

แล้วเรียกมันว่าคุณต้องการ:

background {
     //background job
     main {
       //update UI (or what you need to do in main thread)
     }
}

22

คุณต้องแยกการเปลี่ยนแปลงที่คุณต้องการเรียกใช้ในพื้นหลังออกจากการปรับปรุงที่คุณต้องการเรียกใช้บน UI:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)) {
    // do your task

    dispatch_async(dispatch_get_main_queue()) {
        // update some UI
    }
}

ดังนั้นdispatch_async(dispatch_get_main_queue()) { // update some UI }ถูกเรียกเมื่อคำสั่งพื้นหลัง (บล็อกนอก) เสร็จสิ้นการดำเนินการ?
justColbs

นี่ไม่ใช่เฉพาะสำหรับ Swift 2.3 หรือต่ำกว่าเท่านั้น?
Surz

9

คำตอบที่ดีอย่างไรก็ตามฉันต้องการแบ่งปันวิธีการแก้ปัญหา Object Oriented ของฉันสำหรับรุ่นที่รวดเร็ว 55

โปรดตรวจสอบออก: AsyncTask

แรงบันดาลใจจากแนวคิดโดย AsyncTask ของ android ฉันได้เขียนคลาสของตัวเองใน Swift

AsyncTaskช่วยให้สามารถใช้เธรด UI ได้อย่างเหมาะสมและง่ายดาย คลาสนี้อนุญาตให้ดำเนินการเบื้องหลังและเผยแพร่ผลลัพธ์บนเธรด UI

นี่คือตัวอย่างการใช้งานน้อย

ตัวอย่างที่ 1 -

AsyncTask(backgroundTask: {(p:String)->Void in//set BGParam to String and BGResult to Void
        print(p);//print the value in background thread
    }).execute("Hello async");//execute with value 'Hello async'

ตัวอย่างที่ 2 -

let task2=AsyncTask(beforeTask: {
           print("pre execution");//print 'pre execution' before backgroundTask
        },backgroundTask:{(p:Int)->String in//set BGParam to Int & BGResult to String
            if p>0{//check if execution value is bigger than zero
               return "positive"//pass String "poitive" to afterTask
            }
            return "negative";//otherwise pass String "negative"
        }, afterTask: {(p:String) in
            print(p);//print background task result
    });
    task2.execute(1);//execute with value 1

มันมี 2 ประเภททั่วไป:

  • BGParam - ประเภทของพารามิเตอร์ที่ส่งไปยังงานเมื่อดำเนินการ
  • BGResult - ประเภทของผลลัพธ์ของการคำนวณพื้นหลัง

    เมื่อคุณสร้าง AsyncTask คุณสามารถกำหนดประเภทเหล่านั้นให้เป็นสิ่งที่คุณต้องการส่งผ่านเข้าและออกจากงานพื้นหลัง แต่ถ้าคุณไม่ต้องการประเภทเหล่านั้นคุณสามารถทำเครื่องหมายเป็นประเภทที่ไม่ได้ใช้ด้วยการตั้งค่าเป็น: Voidหรือใช้ไวยากรณ์สั้นลง()

เมื่อทำงานแบบอะซิงโครนัสจะดำเนินการผ่าน 3 ขั้นตอน

  1. beforeTask:()->Void เรียกใช้งานบนเธรด UI ก่อนที่จะดำเนินการงาน
  2. backgroundTask: (param:BGParam)->BGResult เรียกใช้บนเธรดพื้นหลังทันทีหลังจาก
  3. afterTask:(param:BGResult)->Void เรียกใช้งานบนเธรด UI พร้อมผลลัพธ์จากงานพื้นหลัง

4
มันใช้งานได้ดีเยี่ยมสำหรับฉัน เยี่ยมมากทำไมไม่ลองใส่ GitHub?
36 โดยการออกแบบ

8

เนื่องจากคำถาม OP ได้รับการตอบไปแล้วฉันต้องการเพิ่มการพิจารณาความเร็ว:

ฉันไม่แนะนำให้ใช้งานกับ. backgroundความสำคัญของเธรดโดยเฉพาะอย่างยิ่งใน iPhone X ที่ดูเหมือนว่าจะมีการจัดสรรงานบนคอร์พลังงานต่ำ

นี่คือข้อมูลจริงจากฟังก์ชั่นการคำนวณอย่างเข้มข้นที่อ่านจากไฟล์ XML (พร้อมบัฟเฟอร์) และทำการแก้ไขข้อมูล:

ชื่ออุปกรณ์ / .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X: 18.7s / 6.3s / 1.8s / 1.8s / 1.8s
  2. iPhone 7: 4.6s / 3.1s / 3.0s / 2.8s / 2.6s
  3. iPhone 5s: 7.3s / 6.1s / 4.0s / 4.0s / 3.8s

โปรดทราบว่าชุดข้อมูลนั้นไม่เหมือนกันสำหรับอุปกรณ์ทั้งหมด มันใหญ่ที่สุดใน iPhone X และเล็กที่สุดใน iPhone 5s


4

สวิฟท์ 5

เพื่อให้ง่ายขึ้นให้สร้างไฟล์ "DispatchQueue + Extensions.swift" ด้วยเนื้อหานี้:

import Foundation

typealias Dispatch = DispatchQueue

extension Dispatch {

    static func background(_ task: @escaping () -> ()) {
        Dispatch.global(qos: .background).async {
            task()
        }
    }

    static func main(_ task: @escaping () -> ()) {
        Dispatch.main.async {
            task()
        }
    }
}

การใช้งาน:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch ใช้ในการจัดการมัลติทาสก์ในแอพ iOS ของเรา

คุณสามารถใช้รหัสนี้

// Using time interval

DispatchQueue.main.asyncAfter(deadline: DispatchTime.now()+1) {
    print("Hello World")
}

// Background thread
queue.sync {
     for i in 0..<10 {
          print("Hello", i)
     }
}

// Main thread
for i in 20..<30 {
     print("Hello", i)
}

ข้อมูลเพิ่มเติมใช้ลิงค์นี้: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

ฟังก์ชันอเนกประสงค์สำหรับเธรด

public enum QueueType {
        case Main
        case Background
        case LowPriority
        case HighPriority

        var queue: DispatchQueue {
            switch self {
            case .Main:
                return DispatchQueue.main
            case .Background:
                return DispatchQueue(label: "com.app.queue",
                                     qos: .background,
                                     target: nil)
            case .LowPriority:
                return DispatchQueue.global(qos: .userInitiated)
            case .HighPriority:
                return DispatchQueue.global(qos: .userInitiated)
            }
        }
    }

    func performOn(_ queueType: QueueType, closure: @escaping () -> Void) {
        queueType.queue.async(execute: closure)
    }

ใช้มันเหมือน:

performOn(.Background) {
    //Code
}

1

ฉันชอบคำตอบของ Dan Beaulieu จริง ๆ แต่มันไม่สามารถใช้งานได้กับ Swift 2.2 และฉันคิดว่าเราสามารถหลีกเลี่ยงการบังคับที่น่ารังเกียจเหล่านั้นได้!

func backgroundThread(delay: Double = 0.0, background: (() -> Void)? = nil, completion: (() -> Void)? = nil) {

    dispatch_async(dispatch_get_global_queue(QOS_CLASS_USER_INITIATED, 0)) {

        background?()

        if let completion = completion{
            let popTime = dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC)))
            dispatch_after(popTime, dispatch_get_main_queue()) {
                completion()
            }
        }
    }
}

0
dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
    // Conversion into base64 string
    self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
})

-3

ใน Swift 4.2 งานนี้

import Foundation

class myThread: Thread
{
    override func main() {
        while(true) {
            print("Running in the Thread");
            Thread.sleep(forTimeInterval: 4);
        }
    }
}

let t = myThread();
t.start();

while(true) {
    print("Main Loop");
    sleep(5);
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.