如何快速使用后台线程?


329

如何快速使用线程?

dispatchOnMainThread:^{

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

}];

您转换哪一部分有困难?
nschum 2014年

2
为什么]最后一行中的分号之前?
akashivskyy 2014年

3
如果您说明自己在哪里受困或需要哪些帮助,这将很有帮助。
nsuinteger 2014年

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

Answers:


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

Swift 1.2之前版本-已知问题

从Swift 1.1开始,Apple未经修改就不支持上述语法。传递QOS_CLASS_BACKGROUND实际上没有用,而是使用Int(QOS_CLASS_BACKGROUND.value)

有关更多信息,请参阅Apple文档。


23
如果有人想要一种更像Swift的语法,我创建了Async,它在语法中添加了一些糖Async.background {}
tobiasdm 2014年

我在xCode 6.0.1和ios 8中使用您的代码。它给出错误为“ QOS_CLASS_BACKGROUND”返回类,并且类型为UInt32,“ dispatch_get_global_queue”需要第一个参数,因为int会导致类型错误。
Zalak Patel 2014年

因此,在Xcode 6.1.1中,使用简单的简单“ QOS_CLASS_BACKGROUND”不会出错。是固定的吗?
卢卡斯·古森

@LucasGoossen是的,已修复。我已经相应地更新了帖子。
tobiasdm 2015年

1
@NikitaPronchik答案不很清楚吗?否则,请随时对其进行编辑。
tobiasdm'3

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

注:雨燕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方法,用于从低级C库访问旧式GCD方法。应该是Swift的标准配置。
Craig Grummitt'9

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格式!
戴尔·克利福德

1
我比任何在世的人都使用扩展程序。但是使用扩展与原始扩展完全没有真正的危险!
Fattie

@Frouo非常优雅,是否可以在4个异步调用全部完成时添加完成处理程序?我知道它有点题外话。
eonist

1
是的,忘记那个链接。您所需要的只是一个调度组-非常非常简单;不用担心!
Fattie

1
@DilipJangid不能,除非您在background关闭中的工作非常非常长(〜=无限)。该方法可以持续有限的时间:后台作业需要执行的时间。因此,completion一旦您的后台作业执行时间+延迟结束,就会立即调用闭包。
2017年

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

本教程中,您还可以在Swift 3中获得有关GDC的一些深入信息。


说过。由于您的答案是最好的,因此我编写了一行代码,显示您如何“完成后回叫”。随时放松或编辑,欢呼声
Fattie

35

来自Jameson Quave的教程

迅捷2

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

3
只是为了澄清,为什么要用它代替公认的答案?这只是一个较旧的API吗?
Sirens

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
rust

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

您必须将要在后台运行的更改与要在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 2015年

这不仅仅适用于Swift 2.3及更低版本吗?
Surz

9

无论如何,好的答案还是,我想分享我的面向对象的解决方案。迅速更新5

请检查一下:AsyncTask

受Android AsyncTask的概念启发,我在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上呢?
由Design制作

8

由于上面已经回答了OP问题,所以我只想添加一些速度方面的考虑:

我不建议以.background线程优先级运行任务,尤其是在iPhone X上,该任务似乎分配在低功耗内核上。

这是来自计算密集型函数的一些实际数据,该函数从XML文件读取(带有缓冲)并执行数据插值:

设备名称/ 装置技术领域 / .utility / .DEFAULT / .userInitiated / .userInteractive

  1. iPhone X:18.7秒/6.3秒/1.8秒/1.8秒/1.8秒
  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);
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.