Hızlı arka plan iş parçacığı nasıl kullanılır?


329

İplik geçirme nasıl hızlı kullanılır?

dispatchOnMainThread:^{

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

}];

Hangi kısmı dönüştürmekte zorlanıyorsunuz?
nschum

2
Neden ]son satırda noktalı virgül var ?
akashivskyy

3
nerede sıkışıp kaldığınızı veya hangi yardıma ihtiyacınız olduğunu açıklamanız yararlı olacaktır.
nsuinteger

4
Size gerçekten yardımcı oluyorsa doğru cevabı kabul etmelisiniz, diğerlerinin de doğru çözümü bulmasına yardımcı olacaktır.
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

Yanıtlar:


708

Swift 3.0 ve üzeri

Swift 3.0'da çok şey modernize edildi. Arka plan iş parçacığında bir şey çalıştırmak şöyle görünür:

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

Hızlı 1.2 ila 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")
    })
})

Ön Swift 1.2 - Bilinen sorun

Swift 1.1'den itibaren Apple, yukarıdaki sözdizimini bazı değişiklikler olmadan desteklemedi. Passing QOS_CLASS_BACKGROUNDaslında işe yaramadı, bunun yerine kullanın Int(QOS_CLASS_BACKGROUND.value).

Daha fazla bilgi için Elma belgelerine bakın


23
Birisi sözdizimi gibi daha Swift isterse, ben oluşturduk zaman uyumsuz sözdizimi gibi bazı şeker eklerAsync.background {}
tobiasdm

Kodunuzu xCode 6.0.1 ve ios 8 kullanıyorum. "QOS_CLASS_BACKGROUND" dönüş sınıfı olarak hata veriyor ve UInt32 türünde ve "dispatch_get_global_queue" int tür hata geliyor gibi 1. parametre gerektirir.
Zalak Patel

Yani Xcode 6.1.1 sadece basit basit "QOS_CLASS_BACKGROUND" kullanmak için bir hata almıyorum. Düzeltildi mi?
Lucas Goossen

@LucasGoossen Evet, düzeltildi. Gönderiyi buna göre güncelledim.
tobiasdm

1
@NikitaPronchik Bu sorunun cevabı net değil mi? Başka bir düzenleme yapmaktan çekinmeyin.
tobiasdm

123

En iyi uygulama, birden çok kez erişilebilen yeniden kullanılabilir bir fonksiyon tanımlamaktır.

YENİDEN KULLANILABİLİR FONKSİYON:

Örneğin, Global İşlev olarak AppDelegate.swift gibi bir yerde.

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

Not: Swift 2.0'da yukarıdaki QOS_CLASS_USER_INITIATED.value değerini QOS_CLASS_USER_INITIATED.rawValue ile değiştirin yerine

KULLANIM:

A. Arka planda bir işlemi 3 saniyelik bir gecikmeyle çalıştırmak için:

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

B. Arka planda bir işlemi çalıştırmak için ön planda bir tamamlama çalıştırın:

    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 saniye geciktirmek için - arka plan parametresi olmadan tamamlama parametresinin kullanılmasına dikkat edin:

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

1
güzel snippet, doğru cevap olmalı. @Dale Clifford
LoVo

Düşük seviyeli C kütüphanesinden eski zamanlı GCD yöntemlerine erişmek için mükemmel yüksek düzeyli modern Swift-y yaklaşımı. Swift'te standart gelmelidir.
Craig Grummitt

2
Çok hoş. Lütfen onaylar mısınız, gecikme yalnızca tamamlama bloğu için geçerlidir. Bu, A.'deki gecikmenin hiçbir etkisi olmadığı ve arka plan bloğunun gecikmeden hemen yürütüldüğü anlamına gelir.
ObjectiveTC

1
Biraz daha hızlı bir sözdizimi if(background != nil){ background!(); }ile değiştirebilmelisiniz background?()?
Simon Bengtsson

1
Swift 3 için bunu güncelleyebilir misiniz? Otomatik dönüştürücü onu dönüştürdü DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {ancak bu bir hata atıyor cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Bir çalışma çözüm bulunana burada ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL

111

Dan Beaulieu'nin swift5'teki yanıtı (ayrıca 3.0.1'den beri çalışıyor).

Hızlı 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()
                })
            }
        }
    }

}

kullanım

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

Şaşırtıcı, Swift 3.0.1 formatına çok güzel güncellediğiniz için teşekkür ederiz!
Dale Clifford

1
Herhangi bir canlıdan daha fazla uzantı kullanıyorum. Ancak, orijinalinden hiç farklı olmayan bir uzantı kullanmanın gerçek bir tehlikesi var!
Şubat'ta Fattie

@Frouo Çok zarif, 4 zaman uyumsuz çağrı tamamlandığında bir tamamlama işleyicisi eklemek mümkün mü? Biraz konu dışı olduğunu biliyorum.
eonist

1
bu bağlantıyı unut. tek ihtiyacınız olan bir sevk grubu - çok çok basit; Hiç merak etmeyin !
Fattie

1
@DilipJangid, backgroundkapanıştaki işiniz çok çok uzun değilse (~ = sonsuz) yapamazsınız . Bu yöntem sonlu bir süre dayanacak şekilde yapılır: arka plan işinizin yürütülmesi gereken zaman. Bu nedenle, completionarka plan iş yürütme süreniz + gecikmesi geçtiğinde kapatma çağrılır.
frouo

42

Swift 3 sürümü

Swift 3, DispatchQueuekuyrukları ve iş parçacıklarını yönetmek için yeni bir sınıf kullanır . Arka plan iş parçacığında bir şey çalıştırmak için şunları kullanırsınız:

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

Veya iki kod satırında bir şey istiyorsanız:

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

Bu derste Swift 3'te GDC hakkında bazı derinlemesine bilgiler de alabilirsiniz .


Dedim. Cevabınız en iyi olduğu için, "bittiğinde nasıl geri aradığınızı" gösteren bir kod satırına girdim. Gevşemek veya düzenlemek için çekinmeyin, alkış
Fattie

35

Gönderen Jameson Quave en öğretici

Hızlı 2

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

3
Sadece açıklığa kavuşturmak için, bu kabul edilen cevap yerine neden kullanılsın? Bu sadece eski bir API mı?
Sirenler

1
@Sirens Bunun <iOS 8'i destekleyen uygulamalar için çok yararlı olacağını düşünürüm.
bperdue

Bunu iOs 8.2 için işlemleri zorlamak için kullanıyorum.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT, QOS_CLASS_DEFAULT'a geri döner. Yani daha yüksek / kabul edilebilir bir sözdizimi olduğunu söyleyebilirsin.
PostCodeism

34

Swift 4.2 ve Xcode 10.1'de

Üç tür Kuyruğumuz var:

1. Ana Kuyruk: Ana kuyruk, sistem tarafından oluşturulan ve uygulama ana iş parçacığıyla ilişkilendirilmiş bir seri kuyruktur.

2. Küresel Sıra: Global kuyruk, görevlerin önceliği ile ilgili talep edebileceğimiz eşzamanlı bir kuyruktur.

3. Özel kuyruklar: kullanıcı tarafından oluşturulabilir. Özel eşzamanlı kuyruklar her zaman bir Hizmet Kalitesi özelliği (QoS) belirterek küresel kuyruklardan birine eşlenir.

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

Bu tüm Kuyruklar iki şekilde yürütülebilir

1. Senkron yürütme

2. Zaman uyumsuz yürütme

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'dan: 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)
    }
}


.background QoS kullandığınızda herhangi bir değişiklik görmedim veya .userInitiatedbenim için işe yaradı.background
rust

24

Swift 4.x

Bunu bir dosyaya koy:

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

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

ve sonra ihtiyacınız olan yerde arayın:

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

22

Arka planda çalıştırmak istediğiniz değişiklikleri kullanıcı arayüzünde çalıştırmak istediğiniz güncellemelerden ayırmanız gerekir:

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

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

Yani dispatch_async(dispatch_get_main_queue()) { // update some UI }arka plan beyanı (Dış Bloğu) yürütme bittiğinde denir?
justColbs

Bu sadece Swift 2.3 ve altı için değil mi?
Surz

9

Yine de iyi yanıtlar, yine de Nesne Odaklı çözümümü paylaşmak istiyorum Hızlı 5 için güncel .

lütfen kontrol et: AsyncTask

Kavramsal olarak Android'in AsyncTask'tan esinlenerek, Swift'te kendi sınıfımı yazdım

AsyncTask , UI iş parçacığının düzgün ve kolay kullanımını sağlar. Bu sınıf, arka plan işlemlerini gerçekleştirmeye ve sonuçları UI iş parçacığında yayınlamaya izin verir.

İşte birkaç kullanım örneği

Örnek 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'

Örnek 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 genel türü vardır:

  • BGParam - yürütme sonrasında göreve gönderilen parametrenin türü.
  • BGResult - arka plan hesaplamasının sonucunun türü.

    Bir AsyncTask oluşturduğunuzda, bu türleri arka plan görevine girip çıkmak için ihtiyacınız olan her şeye yapabilirsiniz, ancak bu türlere ihtiyacınız yoksa, yalnızca şu şekilde ayarlayarak Voidveya daha kısa sözdizimi ile kullanılmamış olarak işaretleyebilirsiniz :()

Eşzamansız bir görev yürütüldüğünde, 3 adımdan geçer:

  1. beforeTask:()->Void görev yürütülmeden hemen önce UI iş parçacığında çağrılır.
  2. backgroundTask: (param:BGParam)->BGResult hemen arka plan iş parçacığında çağrıldı
  3. afterTask:(param:BGResult)->Void arka plan görevinin sonucu ile UI iş parçacığında çağrıldı

4
Bu benim için harika çalışıyor. İyi iş, neden github'a koymuyorsun?
36 By Design

8

OP sorusu yukarıda yanıtlandığı için sadece bazı hız değerlendirmeleri eklemek istiyorum:

.Background ile görevleri çalıştırmanızı önermiyorum özellikle düşük güç çekirdeği üzerinde görev tahsis gibi görünüyor iPhone X iş parçacığı önceliği .

Bir XML dosyasından (arabelleğe alma ile) okuyan ve veri enterpolasyonu yapan hesaplama açısından yoğun bir fonksiyondan bazı gerçek veriler:

Aygıt adı / .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

Veri kümesinin tüm cihazlar için aynı olmadığını unutmayın. İPhone X'in en büyüğü ve iPhone 5'lerin en küçüğü.


4

Hızlı 5

Kolaylaştırmak için, bu içerikle bir "DispatchQueue + Extensions.swift" dosyası oluşturun:

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

Kullanımı:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch, iOS uygulamalarımızda çoklu görevi yürütmek için kullanılır.

Bu kodu kullanabilirsiniz

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

Daha fazla bilgi için bu bağlantıyı kullanın: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

İplik için çok amaçlı işlev

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

Gibi kullanın:

performOn(.Background) {
    //Code
}

1

Dan Beaulieu'nun cevabını gerçekten çok seviyorum, ama Swift 2.2 ile çalışmıyor ve bence bu kötü zorla açılmaları önleyebiliriz!

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'de bu işe yarıyor.

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);
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.