Многопоточный вопрос в Objective-C 2.0 - PullRequest
2 голосов
/ 27 октября 2009

У меня есть основной делегат приложения, который содержит метод, который возвращает объект. Этот делегат приложения работает в главном потоке.

У меня также есть NSOperation, который запускается в другом потоке. Помимо желания иногда вызывать мой метод делегата приложения в моем основном потоке, мне также нужно вызывать его из моего потока NSOperation, чтобы получить объект, который он возвращает. Мой первый вопрос: если я позвоню из другой ветки ...

id newObject = [[[UIApplication sharedApplication] delegate] myMethod];

... будет ли этот метод обрабатываться в том же потоке, что и операция NSOperation, или в том же потоке (основном), в котором включен делегат приложения?

Я также хочу убедиться, что код в myMethod вызывается только один раз за раз моим потоком операций или моим основным потоком. Могу ли я просто создать экземпляр NSLock var в моем делегате приложения и сделать что-то вроде:

-(id)myMethod {
    [myLock lock];
    myObject = // Get or create my object to return
    [myLock unlock];
    return myObject;
}

Спасибо за вашу помощь!

Mike

Ответы [ 3 ]

11 голосов
/ 27 октября 2009

Если вы явно не напишите код, который вызовет выполнение чего-либо в другом потоке, каждый вызов метода будет выполняться непосредственно в потоке, к которому он был вызван. В вызове метода нет магии. Вы можете думать об этом как о наличии той же семантики / ABI, что и при вызове функции C, для целей многопоточности.

Ваш шаблон блокировки будет работать нормально, чтобы обеспечить эксклюзивный доступ к потокам.

Две дополнительные не связанные заметки (потому что так много людей спотыкаются об этом):

  • объявление свойства как atomic имеет мало общего с безопасностью потока. Атомность только гарантирует, что вы получите правильное значение, а не правильное значение (есть разница).

  • автоматически выпущенные объекты никогда не могут безопасно передаваться между потоками. Вам нужен явный retain в потоке-отправителе и возможная балансировка release в потоке-получателе.

3 голосов
/ 27 октября 2009

Вам абсолютно необходимо выполнить этот вызов в потоке NSOperation, а не просто предоставить требуемый объект как часть создания пользовательской операции?

Если это так, я бы рекомендовал не использовать блокировки, если производительность не является критической. Если iPhone поддерживает это, вы можете использовать Grand Central Dispatch, чтобы получить объект в ваш поток:

__block id newObject = nil;
dispatch_sync(dispatch_get_main_queue(), ^{
    newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain];
});

Для iPhone я бы хотел создать вспомогательный метод:

- (void)createNewObject:(NSValue *)returnPtr {
    id newObject = [[[[UIApplication sharedApplication] delegate] myMethod] retain];
    *(id *)[returnPtr pointerValue] = newObject;
}

И вызывайте это так из вашей NSOperation темы:

id newObject = nil;
[self performSelectorOnMainThread:@selector(createNewObject:)
                       withObject:[NSValue valueWithPointer:&newObject]
                    waitUntilDone:YES];

На самом деле выполнение выполнения в главном потоке имеет меньше неявных рисков.

2 голосов
/ 27 октября 2009

Если вам просто нужно защитить критическую часть кода, почему бы не использовать директиву Objective-C @synchronized? Конечно, использование NSLock также будет работать, но вам нужно явно управлять экземпляром NSLock. Из документации:

Objective-C поддерживает многопоточность в приложениях. Это означает, что два потока могут пытаться изменить один и тот же объект одновременно, что может вызвать серьезные проблемы в программе. Чтобы защитить разделы кода от одновременного выполнения несколькими потоками, Objective-C предоставляет директиву @synchronized ().

Директива @synchronized () блокирует часть кода для использования одним потоком. Другие потоки блокируются, пока поток не выйдет из защищенного кода; то есть, когда выполнение продолжается после последнего оператора в блоке @synchronized ().

Директива @synchronized () принимает в качестве единственного аргумента любой объект Objective-C, включая self. Этот объект известен как семафор взаимного исключения или мьютекс. Это позволяет потоку заблокировать часть кода, чтобы предотвратить его использование другими потоками. Вы должны использовать отдельные семафоры для защиты различных критических разделов программы. Прежде чем приложение станет многопоточным, безопаснее всего создавать все объекты взаимного исключения, чтобы избежать условий гонки.

В листинге 12-1 показан пример кода, который использует self в качестве мьютекса для синхронизации доступа к методам экземпляра текущего объекта. Вы можете использовать аналогичный подход для синхронизации методов класса связанного класса, используя объект Class вместо self. В последнем случае, конечно, только одному потоку одновременно разрешено выполнять метод класса, поскольку существует только один объект класса, который является общим для всех вызывающих.

Листинг 12-1. Блокировка метода с помощью self

- (void)criticalMethod
{
    @synchronized(self) {
        // Critical code.
        ...
    }
}
...