2011-12-27 3 views
3

Я пишу код для рендеринга и поворота изображения, детали которого одновременно вычисляются и обновляются. Он работает без ошибок в одном потоке (с ссылкой на изображение), но выглядит неуклюжим, и я не хочу, чтобы вычисления запускались по ссылке дисплея. Поэтому я хочу выполнить весь код, связанный с OpenGL, в основном потоке (с помощью ссылки на изображение) и все вычисления во втором потоке (выполнение цикла while (YES)).Почему я не использую NSLock?

Я реализовал это с помощью NSThread. Он работает красиво некоторое время, а затем не работает с «Thread 1: Program received signal:« EXC_BAD_ACCESS »во время glDrawArrays и иногда имеет странные вспышки графики. Это то, что я ожидал, если основной поток читает данные на уровне модели, в то время как второй поток переписывал его.

Затем я определил NSLock в объекте модели и заблокировал его для всех записей (в моем классе модели) и чтения (в моем классе вида) ... но он все равно может привести к той же ошибке, а графика все еще иногда странные вспышки.

Я сделал что-то неправильно здесь, или это моя проблема в другом месте?

Во-вторых, что является правильным способом остановить второй поток в этом случае? Ссылка на класс NSThread предполагает использование отмены, проверка isCancelled и выход из нее, но это также говорит о том, что следует избегать выхода из вызова.

Вот модификации коды - в моем классе контроллера (я использую XCode 4.2 с ARC, все мои Ивары являются неатомическими):

@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'? –

+0

Вызов 'glDrawArrays' находится в классе представления, в другом методе (' render'); он вообще не относится к модели. –

+0

Я понял, что одна проблема с моим кодом заключается в том, что метод modelUpdated может обновлять структуры OpenGL, в то же время вызывается метод render, поскольку NSNotificationCentre может вызывать его из любого потока. Поэтому теперь я вставил вторую блокировку в modelUpdated, а также поместил ее в метод визуализации вокруг 'glDrawArrays'. Но я все равно получаю ту же ошибку. –

ответ

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 и перезапустить любое использование Queue 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); 

Это будет использовать намного более низкие затраты на процессор.

+0

Спасибо - я очень ценю вашу помощь - мне было интересно, поможет ли мне GCD. Ваш первый почти комментарий в скобках «не знаю, почему вы не наследуете от NSController» заставило меня понять, как много мне нужно узнать о Cocoa ... и, прочитав больше, подумал о рефакторинге использования KVO и, в частности, шаблона рецепции , что, похоже, подходит для этой проблемы. Спасибо, что показал мне свет! Описание Apple в шаблоне «Приемник» в «Руководстве по основам какао» включает в себя некоторый код для выполнения всех потоков, используя «NSOperationQueue» ... –

Смежные вопросы