为什么我不能停止 dispatch_async 串行队列中的计时器?
Why can not I stop a timer in dispatch_async serial queue?
这只是一个实验代码,但我很困惑,因为代码没有按我的预期执行。
代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.myQueue = dispatch_queue_create("com.maxwell.timer", NULL);
dispatch_async(self.myQueue, ^{
self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"Hey!");
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
现在,我每 1 秒得到一个输出 "Hey!",这里没问题。我知道在分派的线程中我必须 运行 运行 显式循环。
当我试图停止定时器时,问题就出来了。
- (void)stopTimer {
dispatch_async(self.myQueue, ^{
[self.timer invalidate];
self.Timer = nil;
});
}
实际上块中的代码甚至不会执行!
另外,如果我这里使用并发队列(dispatch_asyn(dispatch_get_global_queue(...), ^{...})
)就可以了。
我知道的事情:每次我dispatch_async
,无论是并发队列还是串行队列,代码都在不同的线程中执行。所以严格来说我没有在我添加它的同一个线程中使计时器无效,但它确实在并发线程中无效。
所以我的问题是为什么它无法在串行队列中失效?
我猜:
在串行队列中,任务只有在其前置任务完成后才能执行。这里因为触发计时器的 runloop 是 运行,使计时器无效的任务正在等待(阻塞)。所以代码块永远不会被执行。
问题是您有一个串行队列,您在其上调用 [[NSRunLoop currentRunLoop] run]
. But you’re not returning from that call (as long as there are timers and the like on that run loop). As the run
documentation 说:
If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the NSDefaultRunLoopMode
by repeatedly invoking runMode:beforeDate:
. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.
这会阻塞串行队列的线程。只要该线程被阻塞,任何分配到该队列的代码(例如您尝试使计时器无效)都不会 运行。你有一个“第 22 条军规”。
最重要的是,如果您要为 运行 a NSTimer
设置一个后台线程,您需要为此创建自己的线程,而不是使用其中之一GCD 工作线程。有关示例,请参见 。但是正如该答案继续描述的那样,在后台线程上设置 运行 定时器的首选方法是调度定时器,让您摆脱操纵线程和 运行 循环的杂草。
这只是一个实验代码,但我很困惑,因为代码没有按我的预期执行。
代码如下:
- (void)viewDidLoad {
[super viewDidLoad];
self.myQueue = dispatch_queue_create("com.maxwell.timer", NULL);
dispatch_async(self.myQueue, ^{
self.timer = [NSTimer timerWithTimeInterval:1 repeats:YES block:^(NSTimer * _Nonnull timer) {
NSLog(@"Hey!");
}];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
[[NSRunLoop currentRunLoop] run];
});
}
现在,我每 1 秒得到一个输出 "Hey!",这里没问题。我知道在分派的线程中我必须 运行 运行 显式循环。
当我试图停止定时器时,问题就出来了。
- (void)stopTimer {
dispatch_async(self.myQueue, ^{
[self.timer invalidate];
self.Timer = nil;
});
}
实际上块中的代码甚至不会执行!
另外,如果我这里使用并发队列(dispatch_asyn(dispatch_get_global_queue(...), ^{...})
)就可以了。
我知道的事情:每次我dispatch_async
,无论是并发队列还是串行队列,代码都在不同的线程中执行。所以严格来说我没有在我添加它的同一个线程中使计时器无效,但它确实在并发线程中无效。
所以我的问题是为什么它无法在串行队列中失效?
我猜:
在串行队列中,任务只有在其前置任务完成后才能执行。这里因为触发计时器的 runloop 是 运行,使计时器无效的任务正在等待(阻塞)。所以代码块永远不会被执行。
问题是您有一个串行队列,您在其上调用 [[NSRunLoop currentRunLoop] run]
. But you’re not returning from that call (as long as there are timers and the like on that run loop). As the run
documentation 说:
If no input sources or timers are attached to the run loop, this method exits immediately; otherwise, it runs the receiver in the
NSDefaultRunLoopMode
by repeatedly invokingrunMode:beforeDate:
. In other words, this method effectively begins an infinite loop that processes data from the run loop’s input sources and timers.
这会阻塞串行队列的线程。只要该线程被阻塞,任何分配到该队列的代码(例如您尝试使计时器无效)都不会 运行。你有一个“第 22 条军规”。
最重要的是,如果您要为 运行 a NSTimer
设置一个后台线程,您需要为此创建自己的线程,而不是使用其中之一GCD 工作线程。有关示例,请参见