Мьютекс блокирует только основной поток, когда он достигает своего вызова с помощью директивы @synchronized.

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

@implementation Application

#pragma mark -
#pragma mark Static Initializer
static NSString * SubmitChangesLock = nil;

+ (void)initialize {
    [super initialize];
    SubmitChangesLock = [[NSString alloc] initWithString:@"Submit-Changes-Lock"];
}

+ (NSString *)submitChangesLock {
    return SubmitChangesLock;
}

@end

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

- (void)method1FromClass1 {
    @synchronized ([Application submitChangesLock]) {
        // write to the database here...
    }
}

- (void)method2FromClass2 {
    @synchronized ([Application submitChangesLock]) {
        // write to the database here...
    }
}

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

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

РЕДАКТИРОВАТЬ: я попытался заменить директиву @synchronized с помощью PerformSelectorOnMainThread:waitUntilDone:

- (void)writeToDatabase {
    // write to the database here...
}
- (void)method2FromClass2 {
    [self performSelectorOnMainThread:@selector(writeToDatabase) withObject:nil waitUntilDone:YES];
}

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

Любая помощь будет принята с благодарностью, и большое спасибо заранее.


person Mousa    schedule 04.01.2012    source источник
comment
Есть ли у вас какая-то конкретная причина избегать встроенных опций Apple для многопоточности (например, Grand Central Dispatch, NSOperation/NSOperationQueue)?   -  person paulbailey    schedule 04.01.2012
comment
На самом деле да, мне нужно заблокировать поток, который вызывает любой из этих методов, пока он не завершит свое выполнение, я редактирую свой вопрос, чтобы показать другое решение, которого я пытаюсь избежать.   -  person Mousa    schedule 04.01.2012
comment
Просто из интереса, почему бы не static NSString * SubmitChangesLock = @"Submit-Changes-Lock"; вместо того, чтобы поиграться с методом инициализации?   -  person deanWombourne    schedule 04.01.2012
comment
@Mousa - вы говорите, что блокируете любой поток, который вызывает этот метод, - блокирует даже основной поток? Я бы серьезно посмотрел на использование NSOperationQueue с maxConcurrency, установленным на 1, и на то, чтобы архитектура вашего приложения работала асинхронно.   -  person deanWombourne    schedule 04.01.2012
comment
Я не пробовал, но, думаю, я использовал это таким образом, потому что эта переменная должна сохраняться в течение всего времени жизни приложения, и я хочу избежать сборки мусора, поскольку она автоматически выпускается, я не уверен, как мусор- коллектор имеет дело со статическими переменными.   -  person Mousa    schedule 04.01.2012
comment
@deanWombourne - мне нужно заблокировать поток, который вызывает любую из этих функций, пока он не завершится, но моя проблема в том, что иногда он продолжает блокировать основной поток, хотя выполнение завершено.   -  person Mousa    schedule 04.01.2012
comment
Что ж, поскольку сборщика мусора нет, с вами, вероятно, все будет в порядке :) Строки, объявленные буквально таким образом, не освобождаются автоматически, что, вероятно, вас и беспокоит.   -  person deanWombourne    schedule 04.01.2012


Ответы (1)


В iOS есть функции, которые помогают вам избежать работы с потоками/блокировками в простых и умеренно сложных ситуациях.

Если вы настроите NSOperationQueue и setMaxConcurrentOperationCount: на 1, как предложил deanWombourne, вы можете переложить всю работу на фоновый поток. Есть даже удобный класс (NSInvocationOperation), позволяющий легко повторно использовать ваш код из существующих классов в очереди.

Если эти методы, работающие в фоновом режиме, повлияют на то, что отображается в пользовательском интерфейсе, вы всегда можете использовать performSelectorOnMainThread:withObject:waitUntilDone: для обновления всего необходимого.

Если вы сделаете это так, вы никогда не будете блокировать свой основной поток активностью БД. Поскольку блокировка основного потока замораживает пользовательский интерфейс, это определенно правильный способ.

person paulbailey    schedule 04.01.2012
comment
Я знаю о NSOperationQueue и использую его в своем приложении, но здесь у меня описанная проблема, и я знаю о блокировке основного потока, но он будет заблокирован примерно на короткое время, поэтому мне нужно сохранить поток, вызывающий любую заблокированную функцию, я могу использовать PerformOnMainThread, как описано после редактирования моего вопроса. но я прошу другое решение, а точнее, в чем может быть проблема, что мьютекс не разблокируется. - person Mousa; 04.01.2012
comment
@Mousa, ты нашел основную причину этой проблемы? Я имею в виду причину, по которой мьютекс не разблокирован - person jailani; 02.01.2019
comment
@jailani Мне очень жаль, что это довольно старая проблема, и я на 100% уверен в том, что я в конечном итоге сделал тогда, но я думаю, что внес правку в вопрос, сохранив все взаимодействие с базой данных из одного потока. (основной). - person Mousa; 02.01.2019