¿Cómo usar el hilo de fondo en Swift?


329

¿Cómo usar el enhebrado en Swift?

dispatchOnMainThread:^{

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

}];

¿Qué parte tiene problemas para convertir?
nschum

2
¿Por qué tienes ]antes del punto y coma en la última línea?
akashivskyy 05 de

3
Sería útil si explicas dónde estás atascado o con qué necesitas ayuda.
nsuinteger

44
Debe aceptar la respuesta correcta si realmente le ayuda, también ayudará a otros a encontrar la solución correcta.
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

Respuestas:


708

Swift 3.0+

Mucho se ha modernizado en Swift 3.0. Ejecutar algo en el hilo de fondo se ve así:

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 a 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 - Problema conocido

A partir de Swift 1.1, Apple no admitía la sintaxis anterior sin algunas modificaciones. Pasar en QOS_CLASS_BACKGROUNDrealidad no funcionó, en lugar de usar Int(QOS_CLASS_BACKGROUND.value).

Para obtener más información, consulte la documentación de Apple


23
Y si alguien quiere una sintaxis más Swift, he creado Async que agrega algo de azúcar a la sintaxis comoAsync.background {}
tobiasdm el

Estoy usando su código en xCode 6.0.1 y ios 8. Da error como clase de retorno "QOS_CLASS_BACKGROUND" y es de tipo UInt32 y "dispatch_get_global_queue" requiere el primer parámetro como int, por lo que el error de tipo está por venir.
Zalak Patel

Entonces, en Xcode 6.1.1 no recibo un error por usar simplemente "QOS_CLASS_BACKGROUND". ¿Esta arreglado?
Lucas Goossen

@LucasGoossen Sí, se ha solucionado. He actualizado la publicación en consecuencia.
tobiasdm

1
@NikitaPronchik ¿No está claro esto en la respuesta? De lo contrario, siéntase libre de hacer una edición.
tobiasdm

123

La mejor práctica es definir una función reutilizable a la que se pueda acceder varias veces.

FUNCION REUTILIZABLE:

por ejemplo, en algún lugar como AppDelegate.swift como una función 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?()
        }
    }
}

Nota: en Swift 2.0, reemplace QOS_CLASS_USER_INITIATED.value arriba con QOS_CLASS_USER_INITIATED.rawValue en su lugar

USO:

A. Para ejecutar un proceso en segundo plano con un retraso de 3 segundos:

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

B. Para ejecutar un proceso en segundo plano, ejecute una finalización en primer plano:

    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. Para retrasar 3 segundos: observe el uso del parámetro de finalización sin parámetro de fondo:

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

1
buen fragmento, debería ser la respuesta correcta. @Dale Clifford
LoVo

Enfoque moderno Swift-y brillante de alto nivel para acceder a los métodos GCD antiguos desde la biblioteca C de bajo nivel. Debería venir de serie en Swift.
Craig Grummitt

2
Muy agradable. ¿Podría confirmar que el retraso solo funciona para el bloque de finalización? Eso significa que el retraso en A. no tiene ningún impacto, y el bloque de fondo se ejecuta inmediatamente sin demora.
ObjectiveTC

1
Usted debe ser capaz de reemplazar if(background != nil){ background!(); }con background?()una sintaxis un tanto swiftier?
Simon Bengtsson

1
¿Podría actualizar esto para Swift 3? El convertidor automático lo convirtió DispatchQueue.global(priority: Int(DispatchQoS.QoSClass.userInitiated.rawValue)).async {pero esto arroja un error como cannot invoke initializer for type 'Int' with an argument list of type '(qos_class_t)'. Aquí se encuentra una solución de trabajo ( DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async).
Dev-iL

111

La respuesta de Dan Beaulieu en swift5 (también funciona desde 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()
                })
            }
        }
    }

}

Uso

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

¡Increíble, gracias por actualizar tan bien al formato Swift 3.0.1!
Dale Clifford

1
Yo uso extensiones más que cualquier persona viva. ¡Pero existe un peligro real al usar una extensión que no es diferente, en absoluto, del original!
Fattie

@Frouo Muy elegante, ¿es posible agregar un controlador de finalización para cuando finalicen 4 llamadas asíncronas? Sé que está un poco fuera de tema.
eonista

1
Sí, olvida ese enlace. todo lo que necesitas es un grupo de despacho, es muy muy simple; Sin preocupaciones !
Fattie

1
@DilipJangid no puede, a menos que su trabajo en el backgroundcierre sea muy muy muy largo (~ = infinito). Este método está hecho para durar un tiempo finito: el tiempo que necesita ejecutar su trabajo en segundo plano. Por lo tanto, completionse llamará al cierre tan pronto como haya pasado el tiempo de ejecución del trabajo en segundo plano + el retraso.
Frouo

42

Versión Swift 3

Swift 3 utiliza una nueva DispatchQueueclase para gestionar colas e hilos. Para ejecutar algo en el hilo de fondo usarías:

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

O si quieres algo en dos líneas de código:

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

También puede obtener información detallada sobre GDC en Swift 3 en este tutorial .


Dijo. Como su respuesta es la mejor, agregué una línea de código que muestra cómo "vuelve a llamar cuando haya terminado". Siéntase libre de relajarse o editar, aplausos
Fattie

35

Del tutorial de Jameson Quave

Swift 2

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

3
Solo para aclarar, ¿por qué se usaría esto en lugar de la respuesta aceptada? ¿Es solo una API más antigua?
Sirenas

1
@Sirens Creo que esto sería muy útil para aplicaciones compatibles con <iOS 8.
bperdue

Lo uso para iOs 8.2 para forzar procesos.
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULT vuelve a QOS_CLASS_DEFAULT. Así que supongo que se podría decir que es más de alto nivel / sintaxis aceptada.
PostCodeism

34

En Swift 4.2 y Xcode 10.1

Tenemos tres tipos de colas:

1. Cola principal: La cola principal es una cola en serie creada por el sistema y asociada con el hilo principal de la aplicación.

2. Cola global: la cola global es una cola concurrente que podemos solicitar con respecto a la prioridad de las tareas.

3. Colas personalizadas: pueden ser creadas por el usuario. Las colas concurrentes personalizadas siempre se asignan a una de las colas globales al especificar una propiedad de Calidad de servicio (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

Todas estas colas se pueden ejecutar de dos maneras.

1. Ejecución síncrona

2. Ejecución asincrónica

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

Desde 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
El mejor tutorial para hilos medium.com/@gabriel_lewis/…
iOS

No vi ningún cambio cuando usas .background QoS o, .userInitiatedpero para mí funcionó.background
óxido

24

Swift 4.x

Pon esto en algún archivo:

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

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

y luego llámalo donde lo necesites:

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

22

Debe separar los cambios que desea ejecutar en segundo plano de las actualizaciones que desea ejecutar en la interfaz de usuario:

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

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

Entonces, ¿ dispatch_async(dispatch_get_main_queue()) { // update some UI }se llama cuando la declaración de fondo (Bloque externo) termina de ejecutarse?
justColbs

¿No es esto solo para Swift 2.3 y versiones inferiores?
Surz

9

Sin embargo, buenas respuestas, de todos modos, quiero compartir mi solución orientada a objetos actualizada para Swift 5 .

por favor échale un vistazo: AsyncTask

Conceptualmente inspirado en AsyncTask de Android, escribí mi propia clase en Swift

AsyncTask permite el uso correcto y fácil del hilo de la interfaz de usuario. Esta clase permite realizar operaciones en segundo plano y publicar resultados en el hilo de la interfaz de usuario.

Aquí hay algunos ejemplos de uso

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

Ejemplo 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

Tiene 2 tipos genéricos:

  • BGParam - el tipo de parámetro enviado a la tarea tras la ejecución.

  • BGResult - el tipo del resultado del cálculo de fondo.

    Cuando crea una AsyncTask, puede esos tipos a lo que necesite para pasar dentro y fuera de la tarea en segundo plano, pero si no los necesita, puede marcarlos como no utilizados simplemente configurándolos en: Voido con una sintaxis más corta:()

Cuando se ejecuta una tarea asincrónica, se realizan 3 pasos:

  1. beforeTask:()->Void invocado en el hilo de la interfaz de usuario justo antes de que se ejecute la tarea.
  2. backgroundTask: (param:BGParam)->BGResult invocado en el hilo de fondo inmediatamente después
  3. afterTask:(param:BGResult)->Void invocado en el hilo de la interfaz de usuario con el resultado de la tarea en segundo plano

44
Esto funciona maravillosamente para mí. Buen trabajo, ¿por qué no ponerlo en github?
36 Por diseño

8

Como la pregunta OP ya ha sido respondida anteriormente, solo quiero agregar algunas consideraciones de velocidad:

No recomiendo ejecutar tareas con la prioridad de subproceso .background, especialmente en el iPhone X, donde la tarea parece estar asignada en los núcleos de baja potencia.

Aquí hay algunos datos reales de una función computacionalmente intensiva que se lee desde un archivo XML (con almacenamiento en búfer) y realiza la interpolación de datos:

Nombre del dispositivo / .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

Tenga en cuenta que el conjunto de datos no es el mismo para todos los dispositivos. Es el más grande en el iPhone X y el más pequeño en el iPhone 5s.


4

Swift 5

Para hacerlo más fácil, cree un archivo "DispatchQueue + Extensions.swift" con este contenido:

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

Uso:

Dispatch.background {
    // do stuff

    Dispatch.main { 
        // update UI
    }
}

2

Grand Central Dispatch se usa para manejar la multitarea en nuestras aplicaciones iOS.

Puedes usar este código

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

Más información utilice este enlace: https://www.programminghub.us/2018/07/integrate-dispatcher-in-swift.html


2

Función multipropósito para hilo

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

Úselo como:

performOn(.Background) {
    //Code
}

1

Realmente me gusta la respuesta de Dan Beaulieu, pero no funciona con Swift 2.2 y creo que podemos evitar esos desagradables desenredos forzados.

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

en Swift 4.2 esto funciona.

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);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.