Як швидко використовувати фонову нитку?


329

Як користуватися різьбленням у швидкій формі?

dispatchOnMainThread:^{

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

}];

У якій частині у вас проблеми з перетворенням?
nschum

2
Чому ви маєте ]перед крапкою з комою в останньому рядку?
акашівський

3
Було б корисно, якщо ви поясните, де ви застрягли або для чого вам потрібна допомога.
nsuinteger

4
Ви повинні прийняти правильну відповідь, якщо вона вам справді допомагає, це допоможе і іншим знайти правильне рішення.
Аміт Сінгх

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." } }
Анураг Шарма

Відповіді:


708

Швидкий 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")
    }
}

Швидкий перехід від 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_BACKGROUNDнасправді не спрацювала, натомість використовуйте Int(QOS_CLASS_BACKGROUND.value).

Для отримання додаткової інформації див документацію щодо яблук


23
І якщо хтось хоче більше синтаксису типу Swift, я створив Async, який додає цукру до синтаксису, наприкладAsync.background {}
tobiasdm

Я використовую ваш код у xCode 6.0.1 та ios 8. Він дає помилку як "QOS_CLASS_BACKGROUND" клас повернення, і він типу UInt32, а "dispatch_get_global_queue" вимагає 1-го параметра, як int, так що помилка типу приходить.
Zalak Patel

Так що в Xcode 6.1.1 я не отримую помилки за використання простого "QOS_CLASS_BACKGROUND". Це виправлено?
Лукас Гуссен

@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.value вище 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 з бібліотеки С низького рівня. Має бути стандартним у Swift.
Крейг Груммітт

2
Дуже хороший. Будь ласка, підтвердьте, що затримка працює лише для блоку завершення. Отже, це означає, що затримка в A. не впливає, а блок фону виконується негайно без затримки.
ObjectiveTC

1
Ви повинні бути в змозі замінити if(background != nil){ background!(); }з background?()на кілька swiftier синтаксис?
Саймон Бенгтссон

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

Відповідь Дана Болье у swift5 (працює також з швидкої версії 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!
Дейл Кліффорд

1
Я використовую розширення більше, ніж будь-яка жива людина. Але існує реальна небезпека використання розширення, яке нічим не відрізняється від оригіналу!
Fattie

@Frouo Дуже елегантно, чи можна додати обробник завершення, коли 4 асинхронні дзвінки завершують все? Я знаю, це трохи поза темою.
еоніст

1
ви забудете це посилання. все, що вам потрібно, - це диспетчерська група - це дуже дуже просто; ніяких турбот!
Fattie

1
@DilipJangid ви не можете, якщо ваша робота в backgroundзакритті не дуже-дуже довга (~ = нескінченна). Цей метод триває протягом обмеженого часу: час, який потрібно виконати у фоновому режимі. Отже, completionзакриття буде викликано, як тільки минув ваш час виконання фонового завдання + затримка.
frouo

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

З підручника Джеймсона Куава

Швидкий 2

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

3
Тільки для уточнення, чому б це було використано замість прийнятої відповіді? Це лише старіший API?
Сирени

1
@Sirens Я думаю, що це буде дуже корисно для додатків, що підтримують <iOS 8.
bperdue

Я використовую це для iOs 8.2 для примусових процесів.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT повертається до QOS_CLASS_DEFAULT. Тому я думаю, ви можете сказати, що це синтаксис більш високого рівня / прийнятий.
PostCodeism

34

У Swift 4.2 та Xcode 10.1

У нас є три типи черг:

1. Основна черга: Основна черга - це послідовна черга, яка створюється системою і пов'язана з основним потоком програми.

2. Глобальна черга: глобальна черга - це паралельна черга, яку ми можемо запитати стосовно пріоритету завдань.

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

Швидкий 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

Ви повинні відокремити зміни, які потрібно запустити у фоновому режимі, від оновлень, які потрібно запустити в інтерфейсі користувача:

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 і нижче?
Сурц,

9

Хоча відповіді хороші, все-таки я хочу поділитися своїм сучасним об'єктно-рішенням для швидкого 5 .

будь ласка, перевірте це: AsyncTask

Концептуально натхненний AsyncTask для Android, я написав власний клас у Swift

AsyncTask забезпечує правильне та просте використання потоку інтерфейсу користувача. Цей клас дозволяє виконувати фонові операції та публікувати результати в потоці інтерфейсу користувача.

Ось кілька прикладів використання

Приклад 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 викликається в потоці інтерфейсу користувача безпосередньо перед виконанням завдання.
  2. backgroundTask: (param:BGParam)->BGResult викликається на фоновій нитці відразу після
  3. afterTask:(param:BGResult)->Void викликається в потоці інтерфейсу користувача з результатом фонового завдання

4
Це чудово працює для мене. Приємна робота, чому б не покласти її на github?
36 За

8

Оскільки на питання щодо ОП вже відповіли вище, я просто хочу додати деякі міркування щодо швидкості:

Я не рекомендую виконувати завдання з пріоритетом потоку .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

Мені дуже подобається відповідь Дена Болье, але це не працює зі 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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.