63

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

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

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

4

3 に答える 3

69

Mac および iOS アプリケーション内でこのようなものをかなり定期的に使用する必要があるため、次のヘルパー関数を使用します (最初はこの回答で説明されていました)。

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

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

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

これは、あなたが上で説明したプロセスとほぼ同じです。私は、このようなものを独自に作成した他の数人の開発者と話をしました。

[NSThread isMainThread]をチェックする代わりに使用しました。これは、その関数dispatch_get_current_queue()警告セクションで、ID テストにこれを使用することに対して警告があり、呼び出しが iOS 6 で非推奨になったためです。

于 2012-04-26T21:14:55.400 に答える
2

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

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)
    }
}
于 2016-06-29T13:24:25.437 に答える
0

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

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

したがって、この質問を読んでいる他の人にとっては、同期実行が必要ない場合は、単に使用dispatch_**a**syncするだけで、断続的に発生する可能性のあるデッドロックを回避できます。

于 2016-03-28T17:50:34.810 に答える