@synchronized и dispatch_async

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

Теперь я знаю, что многие из вас могут сказать, что не делайте этого; это занимает слишком много времени; это нехорошая практика и т.д. и т.п. Да, я знаю об этом. Это скорее "что будет?" вопрос.

Поэтому всякий раз, когда я собираюсь изменить объект-одиночку, я помещаю код модификации в блок @synchronized, а затем записываю объект на диск. У меня была мысль использовать dispatch_async для записи объекта в отдельный поток, например так:

//singleton object
id dataStructure;

@synchronized(lockObject)
    {
        //code that modifies singleton object
        //not important

        //sharedFileQueue is a SERIAL queue
        dispatch_async([self sharedFileQueue], ^(void){
            NSError * err;

            NSData * plist = [NSPropertyListSerialization dataWithPropertyList:dataStructure
                                                      format:NSPropertyListBinaryFormat_v1_0
                                                      options:0 error:&err];

            //url to somewhere
            NSURL * url;
            BOOL success = [plist writeToURL:url atomically:YES];
        });

    }

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

Я правильно об этом думаю? Также будет ли параметр атомарно изменяться на YES или NO или сериализация моей очереди отправки гарантирует, что несколько потоков не будут записаны в этот файл одновременно?

Благодарю вас!


person strikerdude10    schedule 03.07.2014    source источник
comment
Вы читали это сообщение? ?   -  person Ryan    schedule 03.07.2014


Ответы (2)


Вам как минимум нужно синхронизировать запись на объект блокировки. Это связано с тем, что асинхронный блок выполняется вне контекста @synchronized{ ... }. Это означает, что что-то еще может изменить данные во время их записи, что может привести к логической несогласованности.

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

  • Поток 1 изменяет объект и помещает запись в очередь.
  • Поток 2 изменяет объект и помещает запись в очередь.
  • Запись потока 1 сохраняет моды из потока 1 и потока 2.
  • Запись потока 2 сохраняет моды из потока 1 и потока 2 (что аналогично последнему шагу).
person JeremyP    schedule 03.07.2014

https://developer.apple.com/library/ios/documentation/General/Conceptual/ConcurrencyProgrammingGuide/OperationQueues/OperationQueues.html

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

//singleton object
id dataStructure;

@synchronized(lockObject)
{
    //code that modifies singleton object
    //not important

    id dataStructureToWrite = [dataStructure copy];
    //sharedFileQueue is a SERIAL queue
    dispatch_async([self sharedFileQueue], ^(void){
        NSError * err;

        NSData * plist = [NSPropertyListSerialization dataWithPropertyList:dataStructureToWrite
                                                                    format:NSPropertyListBinaryFormat_v1_0
                                                                   options:0 error:&err];

        //url to somewhere
        NSURL * url;
        BOOL success = [plist writeToURL:url atomically:YES];
    });

}
person anerevol    schedule 03.07.2014
comment
Разве мне не нужен @synchronized для кода, в котором я изменяю структуру данных? Вы говорите, что я должен поместить код, который изменяет dataStructure внутри блока? - person strikerdude10; 03.07.2014
comment
извините, я не видел, чтобы вы изменяли структуру данных в блоке @synchronized. Я не думаю, что помещать изменения в dispatch_async — хорошая идея. если стоимость копирования dataStructure невелика, вы можете скопировать dataStructure для блока dispatch_async, иначе написанная вами dataStructure может иметь неправильный статус. - person anerevol; 03.07.2014