Objective-C运行循环停止并重新启动方法?

时间:2009-07-18 03:35:11

标签: iphone objective-c multithreading

我曾经认为自己是一个相当聪明的人 苹果的“线程编程指南”,打破了我自我欺骗的自我。

我有一个方法我想在辅助线程上重复运行,在下面的示例中我称之为doStuff:
我希望能够反复停止并开始重复调用此方法。

代码启动线程 如果布尔值stuffToDo为真,则 然后它调用doStuff:
否则
它有一点休息 然后它再次循环,直到我告诉它停止

我目前的代码似乎很浪费,因为即使没有任何事情可以检查'stuffToDo'。

我可以摆脱stuffToDo,只是产生并根据需要取消线程。
这看起来也很浪费,这意味着当我已经运行一个新线程时,我需要注意不要意外产生一个新线程。

我确信在Apple的“线程编程指南”的“运行循环管理”部分可以找到有效解决我的困境的答案。 也许它涉及自定义输入源

但我真的觉得这个文件具有挑战性 就好像这个文档在我的大脑中产生了太多的线程而且计算停止了。

enum eRenderThreadMode
{
    render_not_started,
    render_run,
    render_cancel,
    render_finished
};


- (IBAction) startThread:(id)sender
{
    self.renderThreadMode = render_run;
    label.text = @"doing stuff"; 
    [NSThread detachNewThreadSelector:@selector(keepDoingStuff)  toTarget:self withObject:nil];

}

- (void)keepDoingStuff
{
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

    while (renderThreadMode == render_run) 
    {
        if(stuffToDo)
        {
            [self doStuff];
        }
        else
        {
            [NSThread sleepForTimeInterval:kLittleRest];
        }
    }
    self.renderThreadMode = render_finished;
    [pool release];
}


- (IBAction)stopThread:(id)sender
{
    self.renderThreadMode = render_stop;

    while (self.renderThreadMode == render_cancel) 
    {
        [NSThread sleepForTimeInterval:kLittleRest];
    }
}

2 个答案:

答案 0 :(得分:7)

您可以使用辅助线程休眠的同步对象。 This Apple page表示有一个名为条件的工具可能会做你想要的。使用Condition或类似的同步对象只会在需要完成工作时(或者当线程死亡的时候)唤醒你的线程。

答案 1 :(得分:7)

是的,你是正确的,你想使用runloop,你缺少的是如何设置这一切。我将修改你的帖子并解释发生了什么。不要担心,如果它吓唬你,它是棘手的,有一些你只从经验中学到的陷阱

- (IBAction) startThread:(id)sender
{
    self.renderThreadMode = render_run;
    label.text = @"doing stuff"; 
    self.backgroundThread = [[NSThread alloc] initWithTarget:self selector:@selector(keepDoingStuff) object:nil];
    [self.backgroundThread start];    
}

//Okay, this is where we start changing stuff
- (void)keepDoingStuff
{
        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

        //A runloop with no sources returns immediately from runMode:beforeDate:
        //That will wake up the loop and chew CPU. Add a dummy source to prevent
        //it.

        NSRunLoop *runLopp = [NSRunLoop currentRunLoop];

        NSMachPort *dummyPort = [[NSMachPort alloc] init];
        [runLoop addPort:dummyPort forMode:NSDefaultRunLoopMode];
        [dummyPort release];
        [pool release];

        while (1) 
        {
                NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
                [runLoop runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
                [loopPool drain];
        }
}

好的,所以在这一点上你应该看看上面的代码并思考“嗯,这可能是一个很好的睡眠线程,但它没有做任何事情。这是事实,但因为它有一个活跃的runloop我们可以做任何基于runloop的事情,包括performSelector:onThread:withObject:waitUntilDone:

- (void) doStuffOnBackgroundThread
{
    [self performSelector:@selector(doStff) onThread:self.backgroundThread withObject:nil waitUntilDone:NO];
}

当您在主线程(或任何其他线程)上调用上述方法时,它将编组各种参数并将指定线程的runloop入队,并在必要时将其唤醒。在这种情况下,将导致self.backgroundThread从runMode唤醒:beforeDate:,执行-doStuff,然后循环返回aroound循环并返回睡眠等待runMode:beforeDate:。如果你想能够拆除线程,你可以像在代码中一样在while循环中设置一个变量,但是要记住线程会在睡眠时消失,除非你把它唤醒,所以最好封装一下在一个通过performSelector设置控制变量的方法中:onThread:withObject:waitUntilDone:,作为一个加号,意味着该变量只能从后台线程设置,这简化了同步问题。

好吧,所以我认为这可以解决你的问题,所以有时间制作强制性的插件:你确定要用线程做这个吗? NSOperation和NSOperationQueue可能是一个更简单的解决方案,可以解决所有线程问题,如果您需要做的只是偶尔将某些数据排入队列中。他们将安排工作,管理依赖关系,建立/拆除线程以及处理所有runloop唤醒/睡眠的东西。