GCD и изменяемые объекты - PullRequest
1 голос
/ 13 декабря 2011

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

Я мог бы реализовать методы @synchronized или использовать NSLock, но, поскольку я уже широко использую Grand Central Dispatch, мне было интересно, можно ли там что-нибудь сделать, синхронно вынудив их всех на один поток? Я также не уверен, будут ли вышеуказанные методы работать и как они будут реализованы.

Пример кода ниже. Исключение может возникнуть в методе getObjectArray, так как я мог бы добавить к нему / обновить субобъект / удалить в методе translateToObjectFromDict.

NSMutableDictionary *staticDictionary;

-(NSArray *)getObjectArray{

    NSArray *returnArray = [[[NSArray alloc]init]autorelease];
    returnArray = [self.staticDictionary allValues];
    return returnArray;
}

-(void)translateToObjectFromDict:(NSDictionary *)sourceDictionary{



    dispatch_group_t taskGroup = dispatch_group_create();
    dispatch_queue_t queue = dispatch_get_global_queue(0,0);


    dispatch_group_async(taskGroup,queue,^(){

    for (id key in sourceDictionary) {
        NSDictionary *subDictionary = [sourceDictionary objectForKey:key];
        for (id key in subDictionary) {

            NSArray *theObjData = [subDictionary objectForKey:key];
            if ([theObjData count] == 12) {

               theObj *temptheObj = [[PINKtheObj alloc] initWith......
                    NSString *unihex = temptheObj.unihex;

                    if ([self.staticDictionary objectForKey:unihex]){
                        PINKtheObj *localtheObj = [self.staticDictionary objectForKey:unihex];
                        if (temptheObj.time > localtheObj.time) {
                            [localtheObj updatetheObjWith.......
                        [temptheObj release];
                    }else{
                        [self.staticDictionary setObject:temptheObj forKey:unihex];
                    }
            }

        }
    }
    });

    dispatch_group_notify(taskGroup, queue, ^{
        NSDictionary *getMainDict = [self.mainDictionary objectForKey:@"theObjs"];

        for (id key in self.staticDictionary) {
            if (![getMainDict objectForKey:key]){
                [self.removeArray addObject:[self.staticDictionary objectForKey:key]];
            }
        }

        [[NSNotificationCenter defaultCenter]postNotificationName:@"updated" object:nil];
    });
    dispatch_release(taskGroup);

}

Ответы [ 2 ]

3 голосов
/ 13 декабря 2011

Я думаю, что вы должны передавать все свои звонки (objectForKey:, setObject:ForKey: и т. Д.) На staticDictionary в своей собственной очереди. Создайте его, когда синглтон инициализирует, и используйте везде.
В вашем заголовочном файле создайте ivar.

dispatch_queue_t queueDict;

В файле .m просто добавьте к init

queueDict = dispatch_queue_create("com.yourid.yoursingleton.queueDict ", NULL);

Если вы хотите получить доступ или поместить данные в словарь, просто наберите

dispatch_async(queueDict, ^(){
[staticDict setObject:obj forKey:key]; //example
}

Вы можете использовать dispatch_sync, если хотите, чтобы ваша задача ожидала возвращаемого значения.

__block id returnValue;
dispatch_sync(queueDict, ^(){
returnValue = [statictDict objectForKey:key]; //example
}
obj = returnValue;
0 голосов
/ 08 августа 2016

Инкапсулируйте каждый запрос на доступ к словарю в объекте NSBlockOperation или NSInvocation, затем добавьте операцию в NSOperationQueue, установив максимальное количество одновременных операций равным 1:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
[queue setMaxConcurrentOperationCount:1];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Doing something...");
    }];
    
    [queue addOperation:operation];

Если существует согласованный шаблон доступа, например, присущий реализациям, использующим шаблон проектирования Producer-Consumer, вы можете создать повторно используемый объект NSBlockOperation, отделяя каждый вызов для доступа к словарю в их собственном блоке выполнения:

NSOperationQueue *queue = [[NSOperationQueue alloc] init];
    NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{
        NSLog(@"Doing something...");
    }];
    
    //you can add more blocks
    [operation addExecutionBlock:^{
        NSLog(@"Another block");
    }];
    
    [operation setCompletionBlock:^{
        NSLog(@"Doing something once the operation has finished...");
    }];
    
    [queue addOperation:operation];

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

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

Наконец, создайте метод глобального метода доступа, добавив словарь в AppDelegate, который:

  1. Внедряет синглтон

  2. Выполняет инициализацию только после того, как метод или класс впервые ссылаются на словарь

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

Вот еще один вариант, который Apple предлагает в своей документации для разработчиков (я скопировал это непосредственно из Руководство по программированию параллелизма ):

Использование диспетчерских семафоров для регулирования использования ограниченных ресурсов, если задачи, которые вы отправляете в очереди отправки, имеют некоторый конечный доступ ресурс, вы можете использовать диспетчерский семафор для регулирования количество задач, одновременно получающих доступ к этому ресурсу. Отправка Семафор работает как обычный семафор с одним исключением. когда ресурсы доступны, получение посылки занимает меньше времени семафор, чем для приобретения традиционного системного семафора. это потому что Grand Central Dispatch не вызывает в ядро для этого конкретного случая. Единственный раз, когда он вызывает в ядре когда ресурс недоступен и система должна парковать нить, пока семафор не будет сигнализировать. Семантика для использования Семафор рассылки выглядит следующим образом: 1. Когда вы создаете семафор (используя функцию dispatch_semaphore_create), вы можете указать положительное целое число, указывающее число доступные ресурсы. 2. В каждой задаче вызовите dispatch_semaphore_wait, чтобы дождаться семафора. 3. Когда вызов ожидания вернется, приобретите ресурс и сделайте свою работу. 4. По завершении работы с ресурсом отпустите его и отправьте сигнал семафору, вызвав функцию dispatch_semaphore_signal. Для Пример того, как работают эти шаги, рассмотрим использование файловых дескрипторов в системе. Каждой заявке дается ограниченное количество файлов дескрипторы для использования. Если у вас есть задача, которая обрабатывает большое количество файлы, вы не хотите открывать так много файлов одновременно, что вы запускаете вне файловых дескрипторов. Вместо этого вы можете использовать семафор, чтобы ограничить количество файловых дескрипторов, используемых одновременно код обработки файла. Основные части кода, которые вы бы включили в ваших задачах выглядит следующим образом: // Создать семафор, указав начальный размер пула dispatch_semaphore_t fd_sema = dispatch_semaphore_create (getdtablesize () / 2); // Ждем свободного дескриптор файла dispatch_semaphore_wait (fd_sema, DISPATCH_TIME_FOREVER); fd = open ("/ etc / services", O_RDONLY); // Отпустите дескриптор файла, когда закончите close (fd); dispatch_semaphore_signal (fd_sema); Когда вы создаете семафор, вы укажите количество доступных ресурсов. Это значение становится переменная начального числа для семафора. Каждый раз, когда вы ждете на семафор, функция dispatch_semaphore_wait уменьшает значение переменная на 1. Если полученное значение отрицательно, функция сообщаетядро, чтобы заблокировать ваш поток.С другой стороны, dispatch_semaphore_signalfunction увеличивает переменную count на 1, чтобы указать, что ресурс был освобожден.Если есть задачи, заблокированные и ожидающие ресурса, одна из них впоследствии разблокируется и может выполнять свою работу.

...