在NSOperation中使用NSThread睡眠。

时间:2022-04-22 01:17:31

Working with some code, I'm coming across run loops, which I'm new to, inside NSOperations.

在使用一些代码时,我遇到了运行循环,这是NSOperations内部的新内容。

The NSOperations are busy downloading data - and whilst they are busy, there is code to wait for the downloads to complete, in the form of NSRunLoops and thread sleeping.

NSOperations忙着下载数据——当它们忙的时候,有一些代码等待下载完成,以nsrunloop和线程睡眠的形式。

This code in particular is of interest to me:

我对这段代码特别感兴趣:

while (aCertainConditionIsTrue && [self isCancelled]==NO) {
     if(![[NSRunLoop currentRunLoop] runMode: NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]]){
        [NSThread sleepForTimeInterval:1.0];
     }
}

I've read about the run loops, and runMode:beforeDate: will wait for an input source or a timeout. Although I'm not 100% what counts as an input souce.

我读过run循环和runMode:beforeDate:将等待输入源或超时。虽然我不是百分之百的输入源。

On the first execution of this it always returns NO and hits the sleepForTimeInterval:. Is this bad?

在第一次执行时,它总是返回NO并命中sleepForTimeInterval:。这是坏的吗?

In a particular utility class, it's hitting the sleepForTimeInterval: a lot - once for each thread - which significantly hurts the performance.

在一个特定的实用程序类中,它会按睡眠时间间隔进行:对于每一个线程都有很多次,这将极大地影响性能。

Any better solutions for this, or advice?

有什么更好的解决方法或建议吗?

2 个解决方案

#1


2  

Sleeping locks up the thread. Perhaps you change your code to use performSelector:withObject:afterDelay. That way your thread can continue to run.

睡觉锁住线。也许您可以更改代码,使用performSelector:withObject:afterDelay。这样,您的线程可以继续运行。

    ...
    done = NO;
    [self checkDoneCondition:nil];
    ...

- (void)checkDoneCondition:(id)object {
    if (aCertainConditionIsTrue && [self isCancelled]==NO) {
        if(...) {
            [self performSelector:@selector(checkDoneCondition:) withObject:[con error] afterDelay:1.0];
        } else {
            done = YES;
        }
    }
}

#2


1  

It looks like you need to use a concurrent NSOperation. Here is the relevant part in the Apple docs:

看起来您需要使用并发的NSOperation。以下是苹果文档的相关部分:

In contrast to a non-concurrent operation, which runs synchronously, a concurrent operation runs asynchronously. In other words, when you call the start method of a concurrent operation, that method could return before the corresponding task is completed. This might happen because the operation object created a new thread to execute the task or because the operation called an asynchronous function. It does not actually matter if the operation is ongoing when control returns to the caller, only that it could be ongoing. (...) In a concurrent operation, your start method is responsible for starting the operation in an asynchronous manner. Whether you spawn a thread or call an asynchronous function, you do it from this method. Upon starting the operation, your start method should also update the execution state of the operation as reported by the isExecuting method. You do this by sending out KVO notifications for the isExecuting key path, which lets interested clients know that the operation is now running. Your isExecuting method must also return the status in a thread-safe manner.

与同步运行的非并发操作相比,并发操作是异步运行的。换句话说,当您调用并发操作的start方法时,该方法可以在相应的任务完成之前返回。这可能会发生,因为操作对象创建了一个新线程来执行任务,或者因为操作被称为异步函数。当控件返回给调用者时,操作是否正在进行实际上并不重要,而重要的是它可能正在进行。(…)在并发操作中,您的start方法负责以异步方式启动操作。无论您是派生一个线程还是调用一个异步函数,都是从这个方法中完成的。在启动操作时,您的start方法还应该更新isExecuting方法报告的操作执行状态。为此,您可以为isExecuting密钥路径发送KVO通知,该通知让感兴趣的客户知道操作正在运行。您的isExecuting方法也必须以线程安全的方式返回状态。

(from https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html)

(来自https://developer.apple.com/library/mac/文档/可可/引用/ NSOperation_class /引用/ Reference.html)

In other words, you can override the -start method in your NSOperation subclass, and have ivar for the executing and finished property. This method will start the download in a separate thread. When the download starts, you set the executing flag and trigger KVO. WHen it is finished in this thread, you do the same with finished and executing. It seems complicated but it's actually quite simple.

换句话说,您可以在NSOperation子类中重写-start方法,并为执行和完成属性设置ivar。此方法将在单独的线程中启动下载。当下载开始时,您设置执行标志并触发KVO。当它在这个线程中完成时,您对finished和execution也进行同样的操作。看起来很复杂,但其实很简单。

See also this question on Stack Overflow with a great explanation: Subclassing NSOperation to be concurrent and cancellable

关于堆栈溢出的问题,请参见这个问题,并给出一个很好的解释:子类化NSOperation应该是并发的和可取消的

#1


2  

Sleeping locks up the thread. Perhaps you change your code to use performSelector:withObject:afterDelay. That way your thread can continue to run.

睡觉锁住线。也许您可以更改代码,使用performSelector:withObject:afterDelay。这样,您的线程可以继续运行。

    ...
    done = NO;
    [self checkDoneCondition:nil];
    ...

- (void)checkDoneCondition:(id)object {
    if (aCertainConditionIsTrue && [self isCancelled]==NO) {
        if(...) {
            [self performSelector:@selector(checkDoneCondition:) withObject:[con error] afterDelay:1.0];
        } else {
            done = YES;
        }
    }
}

#2


1  

It looks like you need to use a concurrent NSOperation. Here is the relevant part in the Apple docs:

看起来您需要使用并发的NSOperation。以下是苹果文档的相关部分:

In contrast to a non-concurrent operation, which runs synchronously, a concurrent operation runs asynchronously. In other words, when you call the start method of a concurrent operation, that method could return before the corresponding task is completed. This might happen because the operation object created a new thread to execute the task or because the operation called an asynchronous function. It does not actually matter if the operation is ongoing when control returns to the caller, only that it could be ongoing. (...) In a concurrent operation, your start method is responsible for starting the operation in an asynchronous manner. Whether you spawn a thread or call an asynchronous function, you do it from this method. Upon starting the operation, your start method should also update the execution state of the operation as reported by the isExecuting method. You do this by sending out KVO notifications for the isExecuting key path, which lets interested clients know that the operation is now running. Your isExecuting method must also return the status in a thread-safe manner.

与同步运行的非并发操作相比,并发操作是异步运行的。换句话说,当您调用并发操作的start方法时,该方法可以在相应的任务完成之前返回。这可能会发生,因为操作对象创建了一个新线程来执行任务,或者因为操作被称为异步函数。当控件返回给调用者时,操作是否正在进行实际上并不重要,而重要的是它可能正在进行。(…)在并发操作中,您的start方法负责以异步方式启动操作。无论您是派生一个线程还是调用一个异步函数,都是从这个方法中完成的。在启动操作时,您的start方法还应该更新isExecuting方法报告的操作执行状态。为此,您可以为isExecuting密钥路径发送KVO通知,该通知让感兴趣的客户知道操作正在运行。您的isExecuting方法也必须以线程安全的方式返回状态。

(from https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/NSOperation_class/Reference/Reference.html)

(来自https://developer.apple.com/library/mac/文档/可可/引用/ NSOperation_class /引用/ Reference.html)

In other words, you can override the -start method in your NSOperation subclass, and have ivar for the executing and finished property. This method will start the download in a separate thread. When the download starts, you set the executing flag and trigger KVO. WHen it is finished in this thread, you do the same with finished and executing. It seems complicated but it's actually quite simple.

换句话说,您可以在NSOperation子类中重写-start方法,并为执行和完成属性设置ivar。此方法将在单独的线程中启动下载。当下载开始时,您设置执行标志并触发KVO。当它在这个线程中完成时,您对finished和execution也进行同样的操作。看起来很复杂,但其实很简单。

See also this question on Stack Overflow with a great explanation: Subclassing NSOperation to be concurrent and cancellable

关于堆栈溢出的问题,请参见这个问题,并给出一个很好的解释:子类化NSOperation应该是并发的和可取消的