Основные данные и условия гонки - PullRequest
2 голосов
/ 18 ноября 2011

Вопрос: как сделать изменения слияния Core Date, внесенные в один и тот же NSManagedObject, в двух разных потоках?Потоки изменяют различные атрибуты, и любая комбинация этих атрибутов действительна.

Существует приложение с (как минимум) двумя потоками, потоком пользовательского интерфейса и фоновым потоком .Так называемый Document является подклассом NSManagedObject.Document имеет три атрибута: attrA, attrB и attrC.

Фоновый поток читает attrA и записывает attrB, например:

doc.attbB = md5(doc.attrA);

(на практике это более сложный и более трудоемкий процесс, но вы поняли).

Поток UI показывает все attrA, attrB и attrC пользователю и позволяет пользователю изменять attrA и attrC.(И какое-то время значение attrB недопустимо.)

Я подчеркиваю, что только поток пользовательского интерфейса записывает в attrA и только фоновый поток пишетна attrB.

Теперь пользователь меняет attrC до завершения вычисления attrB. фоновый поток пытается сохранить attrB и получает ошибку.Сейчас я сделал следующее:

if(!saved) {
    // I did try to check that it's *that* kind of error,
    // but that's iOS5-specific, while I need 4.3
    // (comments on error type checking in 4.3 are welcome).
    // Anyway, finally it was this:
    id tmp = doc.attrB;
    [[doc managedObjectContext] refreshObject:doc mergeChanges:NO];
    doc.attrB = tmp;
    BOOL saved = [context save:&error];
    // NSLog if it still failed
}

В общем случае это не сработает.

Сначала, что произойдет, если между этими строками будет сделано больше изменений:

    [[doc managedObjectContext] refreshObject:doc mergeChanges:NO];
    doc.attrB = tmp;
    // more changes happen here!!!
    BOOL saved = [context save:&error];

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

В секунду поток UI также может пытаться что-то сохранить.В одном из классов я вижу код, предназначенный для объединения изменений:

#pragma mark Changes Propagation

- (void)__contextDidSave:(NSNotification*)notification
{
    [parent performSelectorOnMainThread:@selector(__mergeChanges:) withObject:notification waitUntilDone:NO];
}

- (void)__mergeChanges:(NSNotification*)notification
{
    [objectContext mergeChangesFromContextDidSaveNotification:notification];
    if (parent) {
        [parent __mergeChanges:notification];
    }
}

Как я понимаю, __mergeChanges будет выполняться в нужном потоке, но не сразу;возможно, что пользовательский интерфейс будет пытаться сохранить изменения в attrA и attrC после фонового потока , измененного attrB, но до запуска __mergeChanges.

Это классическое условие гонки.

Вопрос: как мне правильно внести изменения в Core Date для одного и того же NSManagedObject в двух разных потоках?(Потоки изменяют различные атрибуты, и любая комбинация этих атрибутов действительна, но Базовые данные не знают об этом.)

1 Ответ

0 голосов
/ 18 ноября 2011

Я не специалист по основным данным, но, как я полагаю, вы можете использовать синхронизированную директиву:

// UI Thread
@synchronized(AN_INSTANCE_OF_ANY_OBJECT) {
    // your first thread's code goes here
}

// Background Thread
@synchronized(THE_SAME_INSTANCE_OF_THE_OBJECT) {
    // your second thread's code goes here
}

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

...