2011-12-27 90 views
3

我正在编写代码来渲染和旋转其细节正在被同时计算和更新的图片。它在单线程(带有显示链接)上无错误地工作,但看起来很笨重,我不希望计算被显示链接触发。所以我想在主线程(显示链接)中执行所有与OpenGL相关的代码,并在第二个线程中执行所有计算(执行一段时间(YES)循环)。为什么我不使用NSLock工作?

我使用NSThread实现了这一点。它在一段时间内效果很好,然后在glDrawArrays过程中出现'线程1:程序接收信号:“EXC_BAD_ACCESS”',并且有时会出现奇怪的闪烁图形。这是我期望的,如果主线程在第二个线程覆盖它的同时读取模型级数据。

然后,我在模型对象中定义了一个NSLock,并将其锁定(在我的模型类中)和阅读(在我的视图类中)...但它仍然会导致相同的错误,并且图形仍然偶尔会有奇怪的闪光。

我在这里做错了什么,或者是我的问题在别的地方?

其次,在这种情况下停止第二个线程的正确方法是什么? NSThread类参考建议使用cancel,检查isCancelled,如果存在则退出,但它也表示应该避免调用退出。

下面是修改代码 - 在我的控制器类(我使用的XCode 4.2与ARC;我所有的ivars都非原子):

@interface MyController : NSObject { 
    NSThread *calcThread; 
    ... 
} 
// (I do not give it an @property or @synthesize line) 

@implementation MyController 
- (void) awakeFromNib { 
    calcThread = [[NSThread alloc] initWithTarget:self 
      selector:@selector(calcLoop:) object:nil]; 
    [calcThread start]; 
    ... 
} 
- (void) calcLoop:(id)arg { 
    @autoreleasepool { 
     while (YES) 
      [myModel calculate]; 
    } 
} 
... 

我把NSLock在我的模型类:

@interface MyModel : NSObject { 
    NSLock* oLock; 
    ... 
} 
@property (nonatomic, strong) NSLock* oLock; 

@implementation myModel 
-(id) init { 
    oLock = [[NSLock alloc] init]; 
    ... 
} 
-(void) changeModelAppearance { 
    [oLock lock]; 
    ... 
    [oLock unlock]; 
} 
... 

在我看来类:

@implementation MyView 
-(void) modelUpdated:(NSNotification *) notification { 
// modelUpdated is called via the NSNotificationCenter 
    MyModel* myModel = (MyModel*) [notification object]; 
    [myModel.oLock lock]; 
    ... // update OpenGL structures with data from myModel 
    [myModel.oLock unlock]; 
} 
... 

谢谢!

+0

你在哪里调用'glDrawArrays'? – 2011-12-27 11:10:11

+0

对'glDrawArrays'的调用在视图类中,在另一个方法中('render');它根本不涉及模型。 – 2011-12-27 21:25:43

+0

我意识到我的代码有一个问题,'modelUpdated'方法可能会在调用'render'方法的同时更新OpenGL结构,因为NSNotificationCentre可以从任何线程调用它。所以我现在在modelUpdated中插入了第二个锁,并将它放在'glDrawArrays'的render方法中。但我仍然得到同样的错误。 – 2012-01-02 05:47:13

回答

3

我想你会在这种情况下使用大中央调度更容易。

@interface MyController : NSObject { // Not use why you're not inheriting from NSController here. 
    dispatch_queue_t calQueue; 
    ... 
} 

- (void) awakeFromNib { 
    calcQueue = dispatch_queue_create("com.yourApp.calc", DISPATCH_QUEUE_SERIAL); 
    dispatch_async(calcQueue, ^{ 
     while(YES) // This will peg the CPU at 100% 
      [myModel calculate]; 
    }); 
} 

模型类

@interface MyModel : NSObject { 
    dispatch_queue_t modelQueue; 
    ... 
} 
@property dispatch_queue_t modelQueue; 

@implementation myModel 
-(id) init { 
    modelQueue = dispatch_queue_create("com.yourApp.model", DISPATCH_QUEUE_SERIAL); 
} 

-(void) dealloc { 
    dispatch_release(modelQueue); 
} 

-(void) changeModelAppearance { 
    dispatch_async(modelQueue, ^{ 
     ... 
    }); 
} 
... 

查看

@implementation MyView 
-(void) modelUpdated:(NSNotification *) notification { 
// modelUpdated is called via the NSNotificationCenter 
    MyModel* myModel = (MyModel*) [notification object]; 
    dispatch_async(model.modelQueue, ^{ 
     ... // update OpenGL structures with data from myModel 
    }); 
} 
... 

要暂停任何队列中,你只需要调用dispatch_suspend,如果你使用一个计时器重新启动任何队列使用dispatch_resume

而不是无限循环,你可以减少你的CPU数量唱。

calcTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)); 
dispatch_source_set_timer(calcTimer, DISPATCH_TIME_NOW, DT, 1000); 
dispatch_source_set_event_handler(calcTimer, ^{ 
    ... 
}); 
dispatch_resume(calcTimer); 

这会使用更低的CPU开销。

+0

谢谢 - 我真的很感谢你的帮助 - 我想知道GCD是否会帮助我。你的第一个几乎括号的评论“不确定你为什么不从NSController继承”让我意识到我对Cocoa了解了多少......在阅读更多内容之后,我是否考虑重构使用KVO,特别是接待员模式,这似乎适合于这个问题。感谢您向我展示灯光!苹果对Cocoa Fundamentals Guide中的Receptionist模式的描述包括一些用于在线程间执行的代码,使用'NSOperationQueue'... – 2012-01-22 11:14:44