現在のスレッドがメインスレッドであるかどうかを確認する


121

現在のスレッドがObjective-Cのメインスレッドであるかどうかを確認する方法はありますか?

このようなことをしたいです。

  - (void)someMethod
  {
    if (IS_THIS_MAIN_THREAD?) {
      NSLog(@"ok. this is main thread.");
    } else {
      NSLog(@"don't call this method from other thread!");
    }
  }

他のスレッドからメソッドを呼び出すことの何が問題になっていますか?
David天宇Wong

回答:



24

メインスレッドでメソッドを実行する場合は、次のことができます。

- (void)someMethod
{
    dispatch_block_t block = ^{
        // Code for the method goes here
    };

    if ([NSThread isMainThread])
    {
        block();
    }
    else
    {
        dispatch_async(dispatch_get_main_queue(), block);
    }
}

5
古い質問への回答は、新しい回答が既存の回答とどのように異なるかを説明することでメリットを得られます。
Jason Aller

1
これはやりすぎです。メインスレッドで何らかの作業を行う必要がある場合、メインスレッドにいるかどうかを確認しても意味がありません。Just doNSOperationQueue.mainQueue().addOperationWithBlock { //your work here }
Eric

3
@Eric同意しますが、すでにメインスレッドにいる場合に、メソッドをすぐに実行したい場合はどうしますか?あなたの提案では、メソッドは常にディスパッチされ、後でメイン操作キューを介して実行されます。
boherna 2016年

@boherna正解です。これには注意が必要です。
Eric

@boherna遅いコメントですが、例のdispatch_sync()代わりにを使用すると、コメントで指摘する点がより強くなりdispatch_async()ます。
カレブ


13

メインスレッドにいるかどうかを知りたい場合は、デバッガーを使用するだけです。興味のある行にブレークポイントを設定し、プログラムがそれに到達したら、これを呼び出します。

(lldb) thread info

これにより、現在のスレッドに関する情報が表示されます。

(lldb) thread info thread #1: tid = 0xe8ad0, 0x00000001083515a0 MyApp`MyApp.ViewController.sliderMoved (sender=0x00007fd221486340, self=0x00007fd22161c1a0)(ObjectiveC.UISlider) -> () + 112 at ViewController.swift:20, queue = 'com.apple.main-thread', stop reason = breakpoint 2.1

値がいる場合queuecom.apple.main-thread、あなたはメインスレッドにしています。


6

次のパターンは、メソッドがメインスレッドで実行されることを保証します。

- (void)yourMethod {
    // make sure this runs on the main thread 
    if (![NSThread isMainThread]) {
        [self performSelectorOnMainThread:_cmd/*@selector(yourMethod)*/
                               withObject:nil
                            waitUntilDone:YES];
        return;
    }
    // put your code for yourMethod here
}

_cmdスニペットをʕ•ᴥ•ʔに貼り付ける方法を自動的に使用します
Albert Renshaw

3

二つの方法。@ranoの答えから、

[[NSThread currentThread] isMainThread] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

また、

[[NSThread mainThread] isEqual:[NSThread currentThread]] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

3
void ensureOnMainQueue(void (^block)(void)) {

    if ([[NSOperationQueue currentQueue] isEqual:[NSOperationQueue mainQueue]]) {

        block();

    } else {

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            block();

        }];

    }

}

これはより安全なアプローチであるため、スレッドではなく操作キューをチェックすることに注意してください


これは受け入れられた答えであるはずです。メインスレッド!=メインキュー
railrayparade

2

Monotouch / Xamarin iOSの場合、次の方法でチェックを実行できます。

if (NSThread.Current.IsMainThread)
{
    DoSomething();
}
else
{
    BeginInvokeOnMainThread(() => DoSomething());
}



0

細部

  • Swift 5.1、Xcode 11.3.1

解決策1.キューを検出する

現在のDispatchQueueを取得しますか?

解決策2.メインキューのみを検出する

import Foundation

extension DispatchQueue {

    private struct QueueReference { weak var queue: DispatchQueue? }

    private static let key: DispatchSpecificKey<QueueReference> = {
        let key = DispatchSpecificKey<QueueReference>()
        let queue = DispatchQueue.main
        queue.setSpecific(key: key, value: QueueReference(queue: queue))
        return key
    }()

    static var isRunningOnMainQueue: Bool { getSpecific(key: key)?.queue == .main }
}

使用法

if DispatchQueue.isRunningOnMainQueue { ... }

サンプル

func test(queue: DispatchQueue) {
    queue.async {
        print("--------------------------------------------------------")
        print("queue label: \(queue.label)")
        print("is running on main queue: \(DispatchQueue.isRunningOnMainQueue)")
    }
}

test(queue: DispatchQueue.main)
sleep(1)
test(queue: DispatchQueue.global(qos: .background))
sleep(1)
test(queue: DispatchQueue.global(qos: .unspecified))

結果(ログ)

--------------------------------------------------------
queue label: com.apple.root.background-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.root.default-qos
is running on main queue: false
--------------------------------------------------------
queue label: com.apple.main-thread
is running on main queue: true

0
Here is a way to detect what the current queue is
extension DispatchQueue {
    //Label of the current dispatch queue.
    static var currentQueueLabel: String { String(cString: __dispatch_queue_get_label(nil)) }

    /// Whether the current queue is a `NSBackgroundActivityScheduler` task.
    static var isCurrentQueueNSBackgroundActivitySchedulerQueue: Bool { currentQueueLabel.hasPrefix("com.apple.xpc.activity.") }

    /// Whether the current queue is a `Main` task.
    static var isCurrentQueueMainQueue: Bool { currentQueueLabel.hasPrefix("com.apple.main-thread") }
}

-2

更新: @demostenで言及されたqueue.hヘッダーによると、それは正しい解決策ではないようです

最初に思いついたのは、必要なときにこの機能が次のようになっていたことです。

dispatch_get_main_queue() == dispatch_get_current_queue();

そして、受け入れられた解決策を見ていました:

[NSThread isMainThread];

鉱山のソリューションは2.5倍速くなります。

PSそして、はい、私はチェックしました、それはすべてのスレッドで機能します


3
理にかなっている-メソッドはobj-cランタイムメッセージングシステムのオーバーヘッドをバイパスします。この手法を使用している場合は、コードの臭いが悪いと思います。おそらく時期尚早な最適化の臭いです。
ArtOfWarfare 2013

4
dispatch_get_current_queue()はiOs 6.0から非推奨
Durai Amuthan.H 2013年

33
:あなたはdispatch_get_current_queue()が定義されているAppleのqueue.hヘッダの記述でこれを読むことができます When dispatch_get_current_queue() is called on the main thread, it may or may not return the same value as dispatch_get_main_queue(). Comparing the two is not a valid way to test whether code is executing on the main thread.
demosten
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.