Swiftでバックグラウンドスレッドを使用する方法


329

迅速にスレッディングを使用するには?

dispatchOnMainThread:^{

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

}];

どの部分が変換に問題がありますか?
nschum 2014年

2
]最終行のセミコロンの前に何があるのですか?
akashivskyy 2014年

3
どこに行き詰まっているのか、どこに助けが必要なのかを説明すると役立つでしょう。
nsuinteger 2014年

4
それが本当に役立つ場合は、正しい答えを受け入れる必要があります。他の人も正しい解決策を見つけるのに役立ちます。
アミットシン2016

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

回答:


708

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

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

詳細については、Appleのドキュメントを参照してください


23
そして、誰かがよりSwiftのような構文が必要な場合は、構文に砂糖を追加するAsyncを作成しましたAsync.background {}
tobiasdm

私は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」を使用してもエラーは発生しません。修正されましたか?
Lucas Goossen、2015年

@LucasGoossenはい、修正されました。私はそれに応じて投稿を更新しました。
tobiasdm 2015

1
@NikitaPronchik答えからこれは明らかではありませんか?それ以外の場合は、自由に編集してください。
tobiasdm 2015

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.valueQOS_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

低レベルのCライブラリから昔ながらのGCDメソッドにアクセスするための、すばらしい高レベルの最新のSwift-yアプローチ。Swiftには標準で搭載されています。
Craig Grummitt、2015

2
非常に素晴らしい。確認してください、遅延は完了ブロックに対してのみ機能します。つまり、Aの遅延は影響を与えず、バックグラウンドブロックは遅延なしですぐに実行されます。
ObjectiveTC

1
あなたは置き換えることができるはずif(background != nil){ background!(); }background?()ややswiftier構文については?
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 2016年

111

Dan Beaulieuのswift5での回答(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()
                })
            }
        }
    }

}

使用法

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

すばらしいです。Swift3.0.1形式にアップデートしていただき、ありがとうございます。
デールクリフォード

1
私は他のどの人よりもエクステンションを使用しています。ただし、元のエクステンションとまったく同じであるエクステンションを使用することには本当に危険があります。
Fattie

@Frouo 4つの非同期呼び出しがすべて終了したときに完了ハンドラを追加することは可能ですか?私はそれが少し話題から外れていることを知っています。
eonist 2017

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

または、2行のコードで何かが必要な場合:

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

ジェイムソンQuaveのチュートリアル

スウィフト2

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

3
明確にするために、なぜこれが受け入れられた回答の代わりに使用されるのですか?これは古いAPIですか?
サイレン

1
アプリは<iOS版8.サポートするために、私は、これは非常に有用であろうと思うだろう@Sirens
bperdue

これをiO 8.2に使用してプロセスを強制します。
μολὼν.λαβέ

DISPATCH_QUEUE_PRIORITY_DEFAULTはQOS_CLASS_DEFAULTに戻ります。だから私はあなたがそれがより高レベル/受け入れられた構文であると言うことができると思います。
PostCodeism

34

Swift 4.2およびXcode 10.1

キューには3つのタイプがあります。

1.メインキュー: メインキューは、システムによって作成され、アプリケーションのメインスレッドに関連付けられたシリアルキューです。

2.グローバルキュー: グローバルキューは、タスクの優先度に関して要求できる並行キューです。

3.カスタムキュー:ユーザーが作成できます。Quality of Serviceプロパティ(QoS)を指定することにより、カスタムコンカレントキューは常にグローバルキューの1つにマップされます。

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

これらすべてのキューは2つの方法で実行できます

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

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

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

良い答えですが、とにかくオブジェクト指向ソリューションを共有したいと思います。Swift5の最新情報ます。

ぜひご覧ください: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に置いてみませんか?
36設計による

8

OPの質問はすでに上記で回答されているので、速度に関する考慮事項を追加したいだけです。

特にタスクが低電力コアに割り当てられているように見えるiPhone Xで、.backgroundスレッド優先順位でタスクを実行することはお勧めしません。

以下は、XMLファイルから(バッファリングを使用して)読み取り、データ補間を実行する計算集中型関数からの実際のデータです。

デバイス名/ .background / .utility / .default / .userInitiated / .userInteractive

  1. iPhone X:18.7秒/ 6.3秒/ 1.8秒/ 1.8秒/ 1.8秒
  2. iPhone 7:4.6秒/ 3.1秒/ 3.0秒/ 2.8秒/ 2.6秒
  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);
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.