调用(NSRunLoop)-run()和CFRunLoopRun()有什么区别

时间:2019-04-23 12:58:38

标签: ios multithreading runloop

我正在学习iOS Runloop。网上的一些文章向我展示了这样的代码:

- (void)memoryIssue {
    for (int i = 0; i < 10000; i++) {
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(runThread) object:nil];
        [thread setName:thread_name];
        [thread start];
        [self performSelector:@selector(stopThread) onThread:thread withObject:nil waitUntilDone:YES];
    }
}

- (void)runThread {
    NSLog(@"current thread = %@", [NSThread currentThread]);
    NSRunLoop *runLoop = [NSRunLoop currentRunLoop];
    static NSMachPort *port;
    if (!port) {
        port = [NSMachPort port];
    }
    [runLoop addPort:port forMode:NSDefaultRunLoopMode];
//    CFRunLoopRun();  //All right
    [runLoop run];  // ⚠️Thread not exit...!
}

- (void)stopThread {
    CFRunLoopStop(CFRunLoopGetCurrent());
    NSThread *thread = [NSThread currentThread];
    [thread cancel];
}

使用CFRunLoopRun()时,一切正常。在每个for循环中,都会创建一个线程,然后退出该线程。但是对于[runLoop run],内存不断增长,最后该应用程序由于“-[NSThread start]:线程创建失败,错误35”(达到线程数上限?)而终止。

**我的问题:

  1. -run()CFRunLoopRun()有什么区别?我认为前者只是后者的包装。

  2. 该代码似乎打算显示退出线程的正确方法。在现实生活中可行吗?**

1 个答案:

答案 0 :(得分:0)

CFRunLoopRun文档告诉我们:

  

当前线程的运行循环以默认模式运行(请参见默认运行循环模式),直到使用CFRunLoopStop停止运行循环或将所有源和计时器从默认运行循环模式中移除为止。

但是run documentation对此没有任何引用。它说:

  

如果没有输入源或计时器附加到运行循环,则此方法立即退出;否则,它将通过反复调用runMode:beforeDate:NSDefaultRunLoopMode中运行接收器。换句话说,这种方法有效地开始了一个无限循环,该循环处理来自运行循环的输入源和计时器的数据。

但是它继续警告:

  

从运行循环中手动删除所有已知的输入源和计时器并不能保证运行循环将退出。 macOS可以根据需要安装和删除其他输入源,以处理针对接收者线程的请求。因此,这些源可能会阻止运行循环退出。

     

如果要终止运行循环,则不应使用此方法。而是使用其他运行方法之一,并循环检查自己的其他任意条件。一个简单的例子是:

BOOL shouldKeepRunning = YES; // global
NSRunLoop *theRL = [NSRunLoop currentRunLoop];
while (shouldKeepRunning && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
     

其中shouldKeepRunning设置为程序中其他位置的NO

我还建议您参考Threading Programming Guide: Terminating a Thread

您问:

  

该代码似乎打算显示退出线程的正确方法。在现实生活中可行吗?

不,现在很少再编写我们自己的NSThread代码了。 Grand Central Dispatch(GCD)消除了所有这些麻烦。它效率更高(因为它已准备好可以使用工作线程池,不需要旋转循环或每个线程都使用NSRunLoop等),并且编写代码也非常容易。我不建议编写NSThread代码,除非存在一些非常具体的问题,您无法使用GCD轻松解决。


顺便说一句,请注意,在编写NSThread代码时,您应该确实拥有线程set up its own autorelease pool(尽管我们将使用@autoreleasepool { ... }代替该指南中概述的模式) 。例如:

- (void)runThread {
    @autoreleasepool {
        ...
    }
}

如果您使用GCD,则此内存管理将由您来照顾。


如果您需要有关NSThreadNSRunLoop等的更多信息,请参见Threading Programming Guide。或为自己省下很多苦头,只需使用GCD