2011-12-27 99 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