检查当前线程是否为主线程


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

从其他线程调用方法有什么问题?
黄天宇

Answers:



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
对旧问题的答案可以从新答案与现有答案有何不同的解释中受益。
杰森·艾勒

1
这太过分了,如果必须在主线程上完成一些工作,那么检查您是否在主线程上是没有用的。只是做NSOperationQueue.mainQueue().addOperationWithBlock { //your work here }
埃里克

3
@Eric我同意,但是如果要在主线程中立即执行该方法怎么办?根据您的建议,该方法总是分派给以后通过主操作队列执行。
boherna '16

@boherna正确,这是需要提防的事情。
埃里克

@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
void ensureOnMainQueue(void (^block)(void)) {

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

        block();

    } else {

        [[NSOperationQueue mainQueue] addOperationWithBlock:^{

            block();

        }];

    }

}

请注意,我检查操作队列,而不是线程,因为这是一种更安全的方法


这应该是公认的答案,主线程!=主队列
Railwayparade

2

两种方式。根据@rano的回答,

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

也,

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

2

对于Monotouch / Xamarin iOS,您可以通过以下方式执行检查:

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

1

迅捷版


if (NSThread.isMainThread()) {
    print("Main Thread")
}


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
从iOs 6.0
Durai Amuthan.H

33
您可以在Apple的queue.h标头说明中阅读此信息,其中定义了dispatch_get_current_queue(): 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.
演示
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.