NSCondition: рекурсивная блокировка? - PullRequest
1 голос
/ 03 февраля 2012

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

-(void) enqueue:(__strong id)value
{
    [_mutex lock];

    while ([self size] == _maxSize) {
        [_mutex wait];
    }

    [_queue enqueue:value];
    [_mutex signal];
    [_mutex unlock];
}

-(id) dequeue
{    
    [_mutex lock];

    while ([self isEmpty]) {
        [_mutex wait];
    }

    id value = [_queue dequeue];
    [_mutex broadcast];

    [_mutex unlock];
    return value;
}

Где _mutex - это NSCondition.Проблемы возникают при использовании методов -isEmpty и -size:

-(int) size
{
    @try {
        [_mutex lock];

        return [_queue size];
    }
    @finally {
        [_mutex unlock];        
    }
}

-(BOOL) isEmpty
{
    @try {
        [_mutex lock];

        return [_queue isEmpty];
    }
    @finally {    
        [_mutex unlock];
    }
}

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

-(void) enqueue:(__strong id)value
{
    while ([self size] == _maxSize) {
        [_mutex lock];
        [_mutex wait];
        [_mutex unlock];
    }

    [_mutex lock];
    [_queue enqueue:value];
    [_mutex signal];
    [_mutex unlock];
}

-(id) dequeue
{
    while ([self isEmpty]) {
        [_mutex lock];
        [_mutex wait];
        [_mutex unlock];
    }

    [_mutex lock]; // when I require the lock here, another thread has already dequeued the object
    id value = [_queue dequeue];
    [_mutex broadcast];

    [_mutex unlock];
    return value;
}

Тогда программа не заходит в тупик, однако к тому времени, когда я повторно получу блокировку, другой работник уже заберет нужный мне объект.Есть мысли о том, как сделать NSCondition рекурсивным?

Ответы [ 2 ]

1 голос
/ 03 февраля 2012

Я обычно использую следующий шаблон:

-(int)primitiveSize
{
  return [_queue size];
}

Методы с префиксом primitive в ObjC (исходя из традиции именования базовых данных) предполагают, что у них нет побочных эффектов, нет забавного бизнеса, нет конверсий, просто дай мне цену. Таким образом, вы можете использовать primitiveSize в тех случаях, когда вы уже получили блокировку, не оставляя инкапсуляцию.

Это намного быстрее, чем создание рекурсивного мьютекса.

0 голосов
/ 13 февраля 2014

Я реализовал замену для класса NSCondition, который реализует рекурсивный мьютекс: https://github.com/bradley219/NSRecursiveCondition

...