Как определить, есть ли у потока блокировка? - PullRequest
1 голос
/ 23 апреля 2011

Я пишу класс Objective-C, который я хочу быть потокобезопасным.Для этого я использую pthreads и pthread_rwlock (использование @synchronized - это излишне, и я хочу узнать немного больше о pthreads).Блокировка инициируется в объектах, обозначенных методом init, и уничтожается в dealloc.У меня есть три метода манипулирования замком;readLock, writeLock, unlock.Эти три метода просто вызывают связанные функции pthread и в настоящее время больше ничего.

Вот два метода объектов, для каждого из которых требуется writeLock:

-(void)addValue:(const void *)buffer
{
    [self writeLock];

    NSUInteger lastIndex = self.lastIndex;
    [self setValue:buffer atIndex:(lastIndex == NSNotFound) ? 0 : lastIndex+1];

    [self unlock];
}


-(void)setValue:(const void *)buffer atIndex:(NSUInteger)index
{
    [self writeLock];
    //do work here
    [self unlock];
}

Сначала будет вызываться setAddValue:получить блокировку записи и затем вызвать setValue:atIndex:, которая также попытается получить блокировку записи.Документация заявляет, что поведение не определено, когда это происходит.Следовательно, как проверить, есть ли у потока блокировка, прежде чем пытаться получить блокировку?

(я мог бы гарантировать, что критическая секция не будет вызывать вызов, который вызовет другой запрос блокировки, но это будет означать повторение кода, и я хочу, чтобы мой код оставался СУХИМЫМ).

Ответы [ 3 ]

2 голосов
/ 23 апреля 2011

Не совсем понятно, какой тип блокировки вы используете.Вы указываете, что используете pthreads и блокировку чтения / записи, поэтому я пришел к выводу, что вы используете pthread_rwlock.

Если это так, то вы должны использовать pthread_rwlock_trywrlock для блокировки,На странице руководства

В случае успеха функции pthread_rwlock_wrlock () и pthread_rwlock_trywrlock () вернут ноль.В противном случае будет возвращен номер ошибки, чтобы указать на ошибку.

И одна из ошибок:

[EDEADLK] Вызывающий поток уже имеет блокировку чтения / записи (для чтения или записи).

Поэтому я считаю, что вы должны быть в состоянии вызвать pthread_rwlock_trywrlock(), и вы либо добьетесь успеха, он вернет EBUSY, если другой поток заблокирован, или вы получите EDEADLK, если текущий потокимеет замок.

0 голосов
/ 24 апреля 2011

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

int wrlock_wrap(pthread_rwlock_t *l, int *cnt)
{
    int r = *cnt ? 0 : pthread_rwlock_wrlocK(l);
    if (!r) ++*cnt;
    return r;
}

int wrunlock_wrap(pthread_rwlock_t *l, int *cnt)
{
    --*cnt;
    return pthread_rwlock_unlock(l);
}

Вы можете хранить счет рядом с pthread_rwlock_t, где бы он ни хранился, например, как член вашей структуры / класса / что угодно.

0 голосов
/ 23 апреля 2011

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

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

Решение, вероятно, состоит в том, чтобы переместить вызовы readLock и writeLock в вызывающие функции, но, не зная больше, невозможно сказать.

(Это также, вероятно, снизит производительность блокировки, сократив общее количество операций, так как вы не будете блокировать, а затем немедленно разблокировать. Возможно, вам не нужно работать напрямую на уровне pthreads.)

...