Синхронизировать блок внутри блока? - PullRequest
2 голосов
/ 18 января 2012

Я играю с блоками в Objective-C, пытаюсь придумать механизм многократного использования, который возьмет произвольный блок кода и объект блокировки, а затем выполнит блок кода в новом потоке, синхронизированном на при условии блокировки. Идея состоит в том, чтобы придумать простой способ переместить все накладные расходы на синхронизацию / ожидание основного потока, чтобы пользовательский интерфейс приложения всегда был отзывчивым.

Код, который я придумал, довольно прост, он выглядит так:

- (void) executeBlock: (void (^)(void))block {
    block();
}

- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
    void(^syncBlock)() = ^{
        @synchronized(lock) {
            block();
        }
    };
    [self performSelectorInBackground:@selector(executeBlock:) withObject:syncBlock];
}

Так, например, у вас могут быть такие методы, которые выглядят так:

- (void) addObjectToSharedArray:(id) theObj {
    @synchronized(array) {
        [array addObject: theObj];
    }
}

- (void) removeObjectFromSharedArray:(id) theObj {
    @synchronized(array) {
        [array removeObject: theObj];
    }
}

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

- (void) addObjectToSharedArray:(id) theObj {
    [self runAsyncBlock:^{
        [array addObject: theObj];
    } withLock: array];
}

- (void) removeObjectFromSharedArray:(id) theObj {
    [self runAsyncBlock: ^{
        [array removeObject: theObj];
    } withLock:array];
}

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

Проблема в том, что этот код падает после executeBlock: без вывода какого-либо вывода, сообщения об ошибке, журнала сбоя или каких-либо других полезных вещей. Есть ли в моем подходе что-то принципиальное? Если нет, какие-либо предложения относительно того, почему это может быть сбой?

Edit:

Интересно, что он работает без сбоев, если я просто сделаю:

- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
    void(^syncBlock)() = ^{
        @synchronized(lock) {
            block();
        }
    };
    syncBlock();
}

Но, конечно, это заблокирует вызывающий поток, который в значительной степени побеждает цель. Возможно ли, что блоки не пересекают границы потоков? Я думаю, что нет, так как это в значительной степени победило бы цель иметь их в первую очередь.

1 Ответ

4 голосов
/ 18 января 2012

не забудьте вызвать [block copy], иначе он не будет правильно сохранен, потому что блок создается в стеке и уничтожается при выходе из области, и если вы не вызовете copy, он не будет перемещен в кучу, даже если retain вызвано.

- (void) runAsyncBlock: (void (^)(void))block withLock:(id)lock {
    block = [[block copy] autorelease];
    void(^syncBlock)() = ^{
        @synchronized(lock) {
            block();
        }
    };
    syncBlock = [[syncBlock copy] autorelease];
    [self performSelectorInBackground:@selector(executeBlock:) withObject:syncBlock];
}
...