web-dev-qa-db-ja.com

デッドロックなしでメインキューに同期的にディスパッチする方法

メインキューのブロックを同期的にディスパッチする必要があります。現在メインスレッドで実行しているかどうかはわかりません。単純なソリューションは次のようになります。

dispatch_sync(dispatch_get_main_queue(), block);

しかし、現在メインキューで実行されているブロック内にいる場合、この呼び出しはデッドロックを作成します。 (同期ディスパッチはブロックの終了を待機しますが、現在のブロックの終了を待機しているため、ブロックは実行を開始しません。)

次のステップは、現在のキューを確認することです。

if (dispatch_get_current_queue() == dispatch_get_main_queue()) {
    block();
} else {
    dispatch_sync(dispatch_get_main_queue(), block);
}

これは機能しますが、見苦しいです。少なくともいくつかのカスタム関数の背後に隠す前に、この問題に対するより良い解決策はありませんか?ブロックを非同期にディスパッチする余裕がないことを強調します。アプリは非同期にディスパッチされたブロックが「遅すぎる」状態で実行される状況にあります。

61
zoul

MacやiOSアプリケーションでこのようなものをかなり定期的に使用する必要があるため、次のヘルパー関数を使用します(元々 this answer で説明されていました):

_void runOnMainQueueWithoutDeadlocking(void (^block)(void))
{
    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(), block);
    }
}
_

あなたが経由して呼び出す

_runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});
_

これはほとんどあなたが上で説明したプロセスであり、私は自分でこのようなものを独自に作成した他のいくつかの開発者と話しました。

dispatch_get_current_queue()をチェックする代わりに_[NSThread isMainThread]_を使用しました。これは、 その関数の注意事項セクション がこれをIDテストに使用することに対して警告していたためです 呼び出しは廃止されましたiOS 6

68
Brad Larson

メインキューまたはメインスレッド(同じではない)で同期するには、次を使用します。

import Foundation

private let mainQueueKey    = UnsafeMutablePointer<Void>.alloc(1)
private let mainQueueValue  = UnsafeMutablePointer<Void>.alloc(1)


public func dispatch_sync_on_main_queue(block: () -> Void)
{
    struct dispatchonce  { static var token : dispatch_once_t = 0  }
    dispatch_once(&dispatchonce.token,
    {
        dispatch_queue_set_specific(dispatch_get_main_queue(), mainQueueKey, mainQueueValue, nil)
    })

    if dispatch_get_specific(mainQueueKey) == mainQueueValue
    {
        block()
    }
    else
    {
        dispatch_sync(dispatch_get_main_queue(),block)
    }
}

extension NSThread
{
    public class func runBlockOnMainThread(block: () -> Void )
    {
        if NSThread.isMainThread()
        {
            block()
        }
        else
        {
            dispatch_sync(dispatch_get_main_queue(),block)
        }
    }

    public class func runBlockOnMainQueue(block: () -> Void)
    {
        dispatch_sync_on_main_queue(block)
    }
}
2
JollyJinx

最近、UIの更新中にデッドロックが発生し始めました。それが私にこのStack Overflowの質問を導き、受け入れられた答えに基づいてrunOnMainQueueWithoutDeadlocking- typeヘルパー関数を実装することにつながりました。

ただし、実際の問題は、UIをブロックから更新するときに、dispatch_syncではなくdispatch_asyncを誤って使用して、UI更新のメインキューを取得していたことです。コード補完を行うのは簡単で、おそらく事後に気付くのは難しいかもしれません。

そのため、この質問を読んでいる他の人:同期実行が不要な場合dispatch_**a**syncを使用するだけで、断続的にヒットするデッドロックを回避できます。

0
pkamb