【问题标题】:How to use background thread in swift?如何在swift中使用后台线程?
【发布时间】:2014-07-26 05:04:13
【问题描述】:

如何在swift中使用线程?

dispatchOnMainThread:^{

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

}];

【问题讨论】:

  • 转换哪个部分有问题?
  • 为什么最后一行的分号前有]
  • 如果您能解释一下您遇到的问题或您需要帮助的地方,将会很有帮助。
  • 如果它真的对你有帮助,你必须接受正确的答案,它也会帮助其他人找到正确的解决方案。
  • 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." } }

标签: ios swift multithreading cocoa-touch dispatch-queue


【解决方案1】:

斯威夫特 3.0+

在 Swift 3.0 中有很多 modernized。在后台队列上运行看起来像这样:

DispatchQueue.global(qos: .userInitiated).async {
    print("This is run on a background queue")

    DispatchQueue.main.async {
        print("This is run on the main queue, after the previous code in outer block")
    }
}

Swift 1.2 到 2.3

let qualityOfServiceClass = QOS_CLASS_USER_INITIATED
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    print("This is run on a 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_USER_INITIATED 并没有实际工作,而是使用Int(QOS_CLASS_USER_INITIATED.value)

欲了解更多信息,请参阅Apples documentation

【讨论】:

  • 我在 xCode 6.0.1 和 ios 8 中使用您的代码。它给出错误为“QOS_CLASS_BACKGROUND”返回类,它是 UInt32 类型,“dispatch_get_global_queue”需要第一个参数作为 int,所以类型错误是来了。
  • 所以在 Xcode 6.1.1 中我没有因为使用简单的“QOS_CLASS_BACKGROUND”而出错。修复了吗?
  • @LucasGoossen 是的,它已被修复。我已经相应地更新了帖子。
  • @NikitaPronchik 答案不是很清楚吗?否则,请随时对其进行编辑。
【解决方案2】:

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 格式!
  • 我使用扩展的次数比任何活着的人都多。但是使用与原版完全不同的扩展程序存在真正的危险!
  • @Frouo 非常优雅,是否可以在 4 个异步调用全部完成时添加完成处理程序?我知道这有点跑题了。
  • 是的,忘记那个链接了。你所需要的只是一个调度组——非常非常简单;完全不用担心!
  • @DilipJangid 你不能,除非你在background 闭包中的工作非常非常长(~=无限)。此方法将持续有限时间:您的后​​台作业需要执行的时间。因此,一旦您的后台作业执行时间+延迟过去,completion 闭包就会被调用。
【解决方案3】:

最佳实践是定义一个可以多次访问的可重用函数。

可重用功能:

例如像 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
    })

【讨论】:

  • nice sn-p,应该是正确的答案。 @戴尔克利福德
  • 出色的高级现代 Swift-y 方法可从低级 C 库访问老式 GCD 方法。应该成为 Swift 的标准。
  • 非常好。请您确认一下,延迟仅适用于完成块。所以这意味着A.中的延迟没有影响,后台块立即执行,没有延迟。
  • 您应该可以将if(background != nil){ background!(); } 替换为background?() 以获得更快捷的语法?
  • 能否请您为 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)' 的错误。找到了一个可行的解决方案here (DispatchQueue.global(qos: DispatchQoS.QoSClass.userInitiated).async)。
【解决方案4】:

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

您还可以在this tutorial 中获取有关 Swift 3 中 GDC 的一些深入信息。

【讨论】:

  • 说。由于您的答案是最好的,所以我输入了一行代码,显示您如何“完成后回电”。随意放松或编辑,干杯
【解决方案5】:

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

【讨论】:

  • 最佳线程教程medium.com/@gabriel_lewis/…
  • 当您使用 .background QoS 或 .userInitiated 时,我没有看到任何变化,但对我来说,使用 .background 时效果很好
  • 您可能看不到使用 .background 和 .userInitiated QoS 之间的任何区别,因为系统可以覆盖您的设置并将 .background QoS 提升为 .userInitiated。这是从主 UI 队列中使用的队列的幕后优化,以使它们与父级的 QoS 匹配。您可以使用 Thread.current.qualityOfService 检查当前线程的 QoS。
  • DispatchQueue.main.async 根本不会立即在 UX 线程上执行任何操作。它只是将要完成的事情堆叠起来,之后,其他正在完成的项目都完成了
【解决方案6】:

来自Jameson Quave's tutorial

斯威夫特 2

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

【讨论】:

  • 只是为了澄清,为什么要使用它而不是接受的答案?这只是一个较旧的 API 吗?
  • @Sirens 我认为这对于支持
  • 我将它用于 iOs 8.2 来强制进程。
  • DISPATCH_QUEUE_PRIORITY_DEFAULT 恢复为 QOS_CLASS_DEFAULT。所以我想你可以说它是更高级/可接受的语法。
  • DispatchQueue 不是线程。
【解决方案7】:

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

【讨论】:

    【解决方案8】:

    斯威夫特 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
        }
    }
    

    【讨论】:

      【解决方案9】:

      您必须将要在后台运行的更改与要在 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 }在后台语句(外块)执行完毕时被调用?
      • 这不只适用于 Swift 2.3 及以下版本吗?
      【解决方案10】:

      由于上面已经回答了 OP 问题,我只想添加一些速度注意事项:

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

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

      设备名称/.background/.utility/.default/.userInitiated/。用户交互

      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 上是最小的。

      【讨论】:

        【解决方案11】:

        虽然答案很好,但无论如何我想分享我的面向对象解决方案最新的 swift 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 时,您可以将这些类型设置为您需要传入和传出后台任务的任何类型,但如果您不需要这些类型,您可以将其标记为未使用,只需将其设置为:@987654326 @ 或更短的语法:()

        当一个异步任务执行时,它会经过 3 个步骤:

        1. beforeTask:()-&gt;Void 在任务执行前在 UI 线程上调用。
        2. backgroundTask: (param:BGParam)-&gt;BGResult 之后立即在后台线程上调用
        3. afterTask:(param:BGResult)-&gt;Void 在 UI 线程上调用,结果来自后台任务

        【讨论】:

        • 这对我来说非常有用。干得好,为什么不放到github上呢?
        【解决方案12】:

        线程的多用途函数

        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
        }
        

        【讨论】:

          【解决方案13】:

          我真的很喜欢 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()
                      }
                  }
              }
          }
          

          【讨论】:

            【解决方案14】:

            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

            【讨论】:

              【解决方案15】:
              dispatch_async(dispatch_get_global_queue(QOS_CLASS_BACKGROUND, 0), {
                  // Conversion into base64 string
                  self.uploadImageString =  uploadPhotoDataJPEG.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.EncodingEndLineWithCarriageReturn)
              })
              

              【讨论】:

                【解决方案16】:

                下面的代码是否有缺点(需要在之后启动前景屏幕时)?

                import Foundation
                import UIKit
                
                class TestTimeDelay {
                
                    static var connected:Bool = false
                    
                    static var counter:Int = 0
                
                    static func showAfterDelayControl(uiViewController:UIViewController) {
                        NSLog("TestTimeDelay", "showAfterDelayControl")
                    }
                    
                    static func tryReconnect() -> Bool {
                        counter += 1
                        NSLog("TestTimeDelay", "Counter:\(counter)")
                        return counter > 4
                    }
                
                    static func waitOnConnectWithDelay(milliseconds:Int, uiViewController: UIViewController) {
                        
                        DispatchQueue.global(qos: .background).async {
                            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + DispatchTimeInterval.milliseconds(milliseconds), execute: {
                                waitOnConnect(uiViewController: uiViewController)
                            })
                        }
                    }
                
                    static func waitOnConnect(uiViewController:UIViewController) {
                
                        connected = tryReconnect()
                        if connected {
                            showAfterDelayControl(uiViewController: uiViewController)
                        }
                        else {
                            waitOnConnectWithDelay(milliseconds: 200, uiViewController:uiViewController)
                        }
                     }
                }   
                

                【讨论】:

                • 更简单???.......
                【解决方案17】:

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

                【讨论】:

                • 我希望这没有被否决。我一直在网上搜索有关如何在没有 GCD 的情况下在 Swift 中创建实际 threads 的示例,但很难找到任何东西。 GCD 不适合我的用例,我发现这个小例子很有帮助。
                猜你喜欢
                • 2016-01-30
                • 1970-01-01
                • 2012-11-26
                • 1970-01-01
                • 1970-01-01
                • 2016-03-30
                • 1970-01-01
                • 2017-11-19
                • 1970-01-01
                相关资源
                最近更新 更多