メインスレッドでタスクを実行するGCD


254

任意のスレッドからのコールバックがあります。このコールバックを受け取ったら、メインスレッドで特定のタスクを実行したいと思います。

私がすでにメインスレッドにいるかどうかを確認する必要がありますか?または、以下のコードを呼び出す前にこのチェックを実行しないことによるペナルティはありますか?

dispatch_async(dispatch_get_main_queue(), ^{
   // do work here
});

116
5年後、私はまだGCDブロックの構文を思い出せず、毎回ここに行き着きません。
SpaceTrucker 2017年

7
@SpaceTrucker-それは私がこのページにいるのと同じ理由です:D
マシュー・コーリー

4
9年後、私はまだこのページから構文をコピーするようになりました。
Osa

回答:


152

いいえ、すでにメインスレッドにいるかどうかを確認する必要はありません。ブロックをメインキューにディスパッチすることで、対応する実行ループが実行されるときに発生する、メインスレッドでシリアルに実行されるようにブロックをスケジュールするだけです。

すでにメインスレッドにいる場合、動作は同じです。ブロックがスケジュールされ、メインスレッドの実行ループが実行されるときに実行されます。


3
質問は、「このチェックを実行しないことによるペナルティ」があるかどうかです...非同期ディスパッチを使用する必要がない場合、パフォーマンスのペナルティがあると思いますか、それとも簡単ですか?
Dan Rosenstark、2011

1
@Yarほとんどの場合、パフォーマンスに顕著な影響はないと思います。GCDは軽量なライブラリです。とは言っても、私は質問を次のように理解しました:「以下のコードを考えると、メインスレッドにいるかどうかを確認する必要がありますか?」

7
ただし、dispatch_syncを使用しているかどうかを確認する必要があります。そうしないと、デッドロックが発生します。
Victor Engel

メインキューにいて、メインキューにディスパッチasyncすると、実行されますが、アクションの予想されるタイミングが台無しになる可能性があります。このようにUIコードとしてviewDidLoad() まで実行していない後のビューが最初に表示されます
pkamb

106

上で説明した非同期ディスパッチの場合、メインスレッドにいるかどうかを確認する必要はありません。Bavariousが示すように、これは単にメインスレッドで実行するためにキューに入れられます。

ただし、a dispatch_sync()を使用して上記を実行しようとし、コールバックがメインスレッド上にある場合、アプリケーションはその時点でデッドロックになります。ここで私の回答これについて説明します。これは、コードを移動したときにこの動作に驚いたため-performSelectorOnMainThread:です。そこで言及したように、ヘルパー関数を作成しました。

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

あなたがいるメソッドが現在メインスレッド上にない場合、メインスレッド上でブロックを同期的に実行し、ブロックしている場合はインラインでブロックを実行します。これを使用するには、次のような構文を使用できます。

runOnMainQueueWithoutDeadlocking(^{
    //Do stuff
});

1
「runOnMainQueueSync」のような名前を付けてみませんか?デッドロックが発生する可能性があり、実際には発生しないという事実は、コード全体に導入したくない種類のことです。いつもありがとうございます。
Dan Rosenstark、2011

2
@Yar-そのヘルパー関数を使用するのではなく、なぜメインキューでブロックを同期的に発射しないのかが明確になるように、その名前を付けました。それは私にもっと思い出させるものでした。
ブラッド・ラーソン

3
この関数でデッドロックすることはまだ可能です。メインキュー同期>その他のキュー同期>メインキューはデッドロックします。
hfossli 2013年

2
@hfossli-はい、すべてのケースを処理するわけではありません。私の使用方法では、常にバックグラウンドシリアルキューの非同期ディスパッチから呼び出すか、メインスレッドのインラインコードを呼び出していました。dispatch_set_specific()あなたが説明する場合に役立つでしょうか:stackoverflow.com/a/12806754/19679
ブラッド・ラーソン

1
[NSThread isMainThread]は多くの場合YESを返し、GCDプログラミングでこのケースをチェックするのは安全とは見なされません。stackoverflow.com/questions/14716334/...
ウィルLarche

57

他の回答が述べたように、メインスレッドからのdispatch_asyncは問題ありません。

ただし、ユースケースによっては、デメリットを考慮する可能性のある副作用があります。ブロックはキューにスケジュールされているため、制御が実行ループに戻るまで実行されず、遅延の影響があります。ブロックの実行。

例えば、

NSLog(@"before dispatch async");
dispatch_async(dispatch_get_main_queue(), ^{
    NSLog(@"inside dispatch async block main thread from main thread");
});
NSLog(@"after dispatch async");

印刷されます:

before dispatch async
after dispatch async
inside dispatch async block main thread from main thread

このため、外側のNSLogの間でブロックが実行されることを期待している場合、dispatch_asyncは役に立ちません。


これは考慮すべき重要なことであると言う必要があります
マルク-アレクサンドルベルベー

チェックしたいので、これはコードがメインスレッドで実行される場合とされない場合があることを意味します。すでにメインスレッドでこの順序で実行されます。そうでない場合、これは保証されません。したがって、マルチスレッドプログラミングを参照するときは、特定の順序で実行されるようにコードを設計することは避けてください。非同期コールバックの方法を使用してみてください。
おっと2016

1

いいえ、メインスレッドにいるかどうかを確認する必要はありません。これはSwiftでこれを行う方法です:

runThisInMainThread { () -> Void in
    runThisInMainThread { () -> Void in
        // No problem
    }
}

func runThisInMainThread(block: dispatch_block_t) {
    dispatch_async(dispatch_get_main_queue(), block)
}

それは私のリポジトリの標準関数として含まれています、それをチェックしてくださいhttps : //github.com/goktugyil/EZSwiftExtensions

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.