在不同的 NSThread/NSRunLoop 中运行和管理 NSTimer

发布于 2024-09-05 22:47:15 字数 1041 浏览 5 评论 0原文

我正在编写一个 Cocoa 应用程序,其 GUI 是在 Interface Builder 中设计的。我需要在不阻塞 UI 的情况下(定期)安排后台活动,因此我在单独的线程中运行它,如下所示:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    [self performSelectorInBackground:@selector(schedule) withObject:nil];
}

- (void) schedule {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

    timer = [[NSTimer scheduledTimerWithTimeInterval:FEED_UPDATE_INTERVAL
                                                  target:activityObj
                                        selector:@selector(run:)
                                        userInfo:nil
                                         repeats:YES]
         retain];

    [runLoop run];
    [pool release];
}

我保留计时器,因此我可以轻松地使其无效并重新安排。

问题:我还必须触发 run: 方法来响应 GUI 事件,因此它是同步的(即“执行活动”按钮)。像这样:

[timer fire];

我也可以使用performSelectorInBackground 来做到这一点,当然它不会阻塞UI。但是这个同步触发在另一个运行循环中运行!所以我不能保证它们不会重叠。如何在同一个运行循环上对所有触发进行排队?

I'm writing a Cocoa application, with a GUI designed in Interface Builder. I need to schedule background activity (at regular intervals) without blocking the UI, so I run it in a separate thread, like this:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    [self performSelectorInBackground:@selector(schedule) withObject:nil];
}

- (void) schedule {
    NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
    NSRunLoop* runLoop = [NSRunLoop currentRunLoop];

    timer = [[NSTimer scheduledTimerWithTimeInterval:FEED_UPDATE_INTERVAL
                                                  target:activityObj
                                        selector:@selector(run:)
                                        userInfo:nil
                                         repeats:YES]
         retain];

    [runLoop run];
    [pool release];
}

I retain the timer, so I can easily invalidate and reschedule.

Problem: I must also fire the run: method in response to GUI events, so it is synchronous (i.e. a "perform activity" button). Like this:

[timer fire];

I could do this with performSelectorInBackground too, and of course it doesn't block the UI. But this synchronous firings run in another runloop! So I have no guarantee that they won't overlap. How can I queue all of my firings on the same runloop?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

别想她 2024-09-12 22:47:15
[timer setFireDate:[NSDate distantPast]];

我通过将过去的日期传递给 setFireDate 将下一个触发日期调整为 ASAP,从而获得了所需的效果。

[timer setFireDate:[NSDate distantPast]];

I obtained the desired effect by adjusting the next fire date to be ASAP, by passing a past date to setFireDate.

最冷一天 2024-09-12 22:47:15

您可以使用经典的并发解决方案:信号量。对于您的情况,最简单的方法是使用 @synchronized 指令。用 @synchronized 包围 run: 方法的整个主体(或者至少是敏感部分)。对于同步对象,我建议您使用特定的 ivar 或静态变量而不是 ActivityObj 的类,以避免死锁。

-(void)run:(id)param {
    // do thread-safe things here
    @synchronized(syncObj) {
        // put your critical section here
    }
    // do more thread-safe things here
}

关键部分的代码不会重叠。

You can use a classic solution for concurrency: semaphore. In your case, the easiest way is to use @synchronized directive. Surround the entire body (or at least, the sensitive part) of run: method with @synchronized. For the synchronization object I suggest you to use a specific ivar or static variable instead of the activityObj's class in order to avoid deadlocks.

-(void)run:(id)param {
    // do thread-safe things here
    @synchronized(syncObj) {
        // put your critical section here
    }
    // do more thread-safe things here
}

Code in critical section won't overlap.

甜扑 2024-09-12 22:47:15

你应该在mainThread上调度NSTimer(并触发计时器来执行选择器——选择器可以在后台线程上执行,因此不会阻塞UI),而不是通过GCD在后台线程上调度NSTimer,因为NSTimer将被添加到一个 NSRunLoop,并且每个 NSRunLoop 都与一个 NSTread 关联。所以当使用GCD时,使用dispatch_after而不是NSTimer来延迟事情发生。

you should schedule NSTimer on mainThread (and fire the timer to perform the selector --- the selector can be execute on background thread, thus do not block UI), rather than schedule NSTimer on a background thread via GCD, because NSTimer will be added to a NSRunLoop, and every NSRunLoop is associated with a NSTread. So when using GCD, use dispatch_after instead of NSTimer to delay the things to happen.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文