Bagaimana cara menggunakan utas latar di cepat?


329

Bagaimana cara menggunakan threading dalam cepat?

dispatchOnMainThread:^{

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

}];

Bagian mana yang Anda punya kesulitan mengonversi?
nschum

2
Mengapa Anda memiliki ]sebelum titik koma di baris terakhir?
akashivskyy

3
akan sangat membantu jika Anda menjelaskan di mana Anda terjebak atau yang Anda perlu bantuan.
nsuinteger

4
Anda harus menerima jawaban yang benar jika itu benar-benar membantu Anda, itu akan membantu orang lain juga untuk menemukan solusi yang benar.
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

Jawaban:


708

Swift 3.0+

Banyak yang telah dimodernisasi di Swift 3.0. Menjalankan sesuatu di utas latar belakang terlihat seperti ini:

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 hingga 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 - Masalah yang diketahui

Pada Swift 1.1, Apple tidak mendukung sintaks di atas tanpa beberapa modifikasi. Lulus QOS_CLASS_BACKGROUNDtidak benar-benar berfungsi, melainkan gunakan Int(QOS_CLASS_BACKGROUND.value).

Untuk informasi lebih lanjut, lihat dokumentasi Apel


23
Dan jika seseorang menginginkan sintaks seperti Swift, saya telah membuat Async yang menambahkan gula ke sintaksis sepertiAsync.background {}
tobiasdm

Saya menggunakan kode Anda di xCode 6.0.1 dan ios 8.In memberikan kesalahan sebagai "QOS_CLASS_BACKGROUND" kelas kembali dan itu adalah tipe UInt32 dan "dispatch_get_global_queue" memerlukan parameter 1 sebagai int sehingga jenis kesalahan datang.
Zalak Patel

Jadi dalam Xcode 6.1.1 saya tidak mendapatkan kesalahan karena menggunakan "QOS_CLASS_BACKGROUND" sederhana saja. Apakah sudah diperbaiki?
Lucas Goossen

@LucasGoossen Ya, sudah diperbaiki. Saya telah memperbarui posnya.
tobiasdm

1
@NikitaPronchik Tidakkah ini jelas dari jawabannya? Lain merasa bebas untuk mengeditnya.
tobiasdm

123

Praktik terbaik adalah mendefinisikan fungsi yang dapat digunakan kembali yang dapat diakses beberapa kali.

FUNGSI YANG DAPAT DIGUNAKAN KEMBALI:

misalnya di suatu tempat seperti AppDelegate.swift sebagai Fungsi Global.

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?()
        }
    }
}

Catatan: di Swift 2.0, ganti QOS_CLASS_USER_INITIATED.value di atas dengan QOS_CLASS_USER_INITIATED.rawValue sebagai gantinya

PEMAKAIAN:

A. Untuk menjalankan proses di latar belakang dengan penundaan 3 detik:

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

B. Untuk menjalankan proses di latar belakang, kemudian jalankan penyelesaian di latar depan:

    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. Menunda 3 detik - perhatikan penggunaan parameter penyelesaian tanpa parameter latar belakang:

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

1
cuplikan yang bagus, harus menjadi jawaban yang benar. @Dale Clifford
LoVo

Pendekatan Swift-y modern tingkat tinggi yang brilian untuk mengakses metode GCD kuno dari pustaka C tingkat rendah. Seharusnya standar di Swift.
Craig Grummitt

2
Sangat bagus. Tolong konfirmasi, penundaan hanya berfungsi untuk blok penyelesaian. Jadi itu berarti bahwa keterlambatan dalam A. tidak memiliki dampak, dan blok latar belakang dieksekusi segera tanpa penundaan.
ObjectiveTC

1
Anda harus dapat menggantinya if(background != nil){ background!(); }dengan background?()sintaks yang lebih cepat?
Simon Bengtsson

1
Bisakah Anda memperbarui ini untuk Swift 3? Konverter otomatis mengubahnya menjadi DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {tetapi ini melempar kesalahan seperti cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Solusi yang berfungsi ditemukan di sini ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL

111

Jawaban Dan Beaulieu di swift5 (juga berfungsi sejak swift 3.0.1).

Swift 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()
                })
            }
        }
    }

}

Pemakaian

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
})

Luar biasa, terima kasih telah memperbarui dengan sangat baik ke format Swift 3.0.1!
Dale Clifford

1
Saya menggunakan ekstensi lebih dari orang hidup mana pun. Tetapi ada bahaya nyata dalam menggunakan ekstensi yang tidak berbeda, sama sekali, dari aslinya!
Fattie

@ Frouo Sangat elegan, apakah mungkin menambahkan handler penyelesaian ketika 4 panggilan async selesai? Saya tahu ini sedikit di luar topik.
eonist

1
yup lupakan tautan itu. yang Anda butuhkan hanyalah grup pengiriman - sangat sederhana; jangan khawatir sama sekali!
Fattie

1
@DilipJangid Anda tidak bisa, kecuali pekerjaan Anda di backgroundpenutupan sangat sangat sangat lama (~ = tak terbatas). Metode ini dibuat untuk bertahan hingga waktu yang terbatas: waktu yang harus dieksekusi oleh pekerjaan latar belakang Anda. Jadi, completionpenutupan akan dipanggil segera setelah waktu pelaksanaan pekerjaan latar belakang Anda + penundaan telah berlalu.
kemari

42

Versi Swift 3

Swift 3 menggunakan DispatchQueuekelas baru untuk mengelola antrian dan utas. Untuk menjalankan sesuatu di utas latar yang akan Anda gunakan:

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

Atau jika Anda menginginkan sesuatu dalam dua baris kode:

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."
    }
}

Anda juga bisa mendapatkan info mendalam tentang GDC di Swift 3 dalam tutorial ini .


Kata. Karena jawaban Anda adalah yang terbaik, saya melemparkan sederet kode yang menunjukkan bagaimana Anda "menelepon kembali ketika selesai". Jangan ragu untuk melepas lelah atau mengedit, bersorak
Fattie

35

Dari tutorial Jameson Quave

Cepat 2

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

3
Hanya untuk klarifikasi, mengapa ini digunakan alih-alih jawaban yang diterima? Apakah ini hanya API yang lebih lama?
Sirene

1
@ Sirens Saya pikir ini akan sangat berguna untuk aplikasi yang mendukung <iOS 8.
bperdue

Saya menggunakan ini untuk 8.2 8.2 untuk memaksa proses.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT kembali ke QOS_CLASS_DEFAULT. Jadi saya kira Anda bisa mengatakan itu sintaks tingkat lebih tinggi / diterima.
PostCodeism

34

Di Swift 4.2 dan Xcode 10.1

Kami memiliki tiga jenis Antrian:

1. Antrian Utama: Antrian utama adalah antrian serial yang dibuat oleh sistem dan dikaitkan dengan utas utama aplikasi.

2. Antrian Global: Antrian global adalah antrian serentak yang dapat kami minta sehubungan dengan prioritas tugas.

3. Antrian khusus: dapat dibuat oleh pengguna. Antrian konkuren kustom selalu dipetakan ke salah satu antrian global dengan menetapkan properti Kualitas Layanan (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

Semua Antrian ini dapat dieksekusi dengan dua cara

1. Eksekusi sinkron

2. Eksekusi asinkron

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()
})

Dari 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
Tutorial terbaik untuk utas media.com/@gabriel_lewis/…
iOS

Saya tidak melihat perubahan ketika Anda menggunakan .background QoS atau .userInitiatedtetapi bagi saya itu berhasil dengan.background
karat

24

Swift 4.x

Letakkan ini di beberapa file:

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

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

dan kemudian menyebutnya di mana Anda butuhkan:

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

22

Anda harus memisahkan perubahan yang ingin Anda jalankan di latar belakang dari pembaruan yang ingin Anda jalankan di UI:

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

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

Jadi dispatch_async(dispatch_get_main_queue()) { // update some UI }dipanggil ketika pernyataan latar belakang (Blok Luar) dilakukan mengeksekusi?
justColbs

Bukankah ini hanya untuk Swift 2.3 dan di bawah?
Surz

9

Namun jawaban yang bagus, bagaimanapun saya ingin membagikan solusi Berorientasi Objek saya yang terbaru untuk yang cepat 5 .

silakan periksa: AsyncTask

Terinspirasi secara konsep oleh Android AsyncTask, saya sudah menulis kelas sendiri di Swift

AsyncTask memungkinkan penggunaan utas UI yang benar dan mudah. Kelas ini memungkinkan untuk melakukan operasi latar belakang dan menerbitkan hasil pada utas UI.

Berikut adalah beberapa contoh penggunaan

Contoh 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'

Contoh 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

Ini memiliki 2 jenis generik:

  • BGParam - jenis parameter yang dikirim ke tugas setelah eksekusi.

  • BGResult - jenis hasil perhitungan latar belakang.

    Saat Anda membuat AsyncTask, Anda bisa mengetikkan itu untuk apa pun yang Anda perlukan untuk masuk dan keluar dari tugas latar belakang, tetapi jika Anda tidak membutuhkan jenis itu, Anda dapat menandainya sebagai tidak digunakan dengan hanya mengaturnya ke: Voidatau dengan sintaks yang lebih pendek:()

Saat tugas asinkron dijalankan, ia melewati 3 langkah:

  1. beforeTask:()->Void dipanggil pada utas UI tepat sebelum tugas dijalankan.
  2. backgroundTask: (param:BGParam)->BGResult dipanggil di utas latar belakang segera setelah
  3. afterTask:(param:BGResult)->Void dipanggil pada utas UI dengan hasil dari tugas latar belakang

4
Ini bekerja sangat baik untuk saya. Kerja bagus, mengapa tidak meletakkannya di github?
36 By Design

8

Karena pertanyaan OP sudah dijawab di atas, saya hanya ingin menambahkan beberapa pertimbangan kecepatan:

Saya tidak merekomendasikan menjalankan tugas dengan prioritas .background thread terutama pada iPhone X di mana tugas tersebut tampaknya dialokasikan pada core berdaya rendah.

Berikut adalah beberapa data nyata dari fungsi intensif komputasi yang membaca dari file XML (dengan buffering) dan melakukan interpolasi data:

Nama perangkat / .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 5: 7.3s / 6.1s / 4.0s / 4.0s / 3.8s

Perhatikan bahwa kumpulan data tidak sama untuk semua perangkat. Ini yang terbesar di iPhone X dan yang terkecil di iPhone 5s.


4

Cepat 5

Untuk membuatnya mudah, buat file "DispatchQueue + Extensions.swift" dengan konten ini:

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()
        }
    }
}

Penggunaan:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch digunakan untuk menangani multitasking di aplikasi iOS kami.

Anda dapat menggunakan kode ini

// 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)
}

Informasi lebih lanjut menggunakan tautan ini: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

Fungsi serba guna untuk utas

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)
    }

Gunakan seperti:

performOn(.Background) {
    //Code
}

1

Saya benar-benar menyukai jawaban Dan Beaulieu, tetapi itu tidak berhasil dengan Swift 2.2 dan saya pikir kita dapat menghindari bungkusan paksa yang tidak enak itu!

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

di Swift 4.2 ini berfungsi.

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);
}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.