Как позволить определенным потокам иметь приоритет при блокировке мьютекса, используя PTHREADS - PullRequest
14 голосов
/ 19 июля 2010

Предположим, что следующий код выполняется 10 потоками.

pthread_mutex_lock(&lock)
Some trivial code
pthread_mutex_unlock(&lock)

Для целей объяснения предположим, что потоками являются T1, T2, T3 ... T10. Мое требование состоит в том, что, пока T1 или T2 или T3 (т. Е. Любой из T1, T2 или T3) ожидает получения блокировки, другие потоки, которые он T4, T5, T6 ..... T10 не должен иметь в состоянии получить блокировка, т. е. T1, T2 и T3, должна иметь приоритет при получении блокировки по отношению к другим потокам.

Полагаю, это можно сделать, увеличив приоритет потоков T1, T2 и T3

.

то есть здесь псевдокод

if this thread is T1 or T2 or T3
increase its priority 
pthread_mutex_lock(&lock)
Some trivial code
pthread_mutex_unlock(&lock)
if this thread is T1 or T2 or T3 decrease it priority to normal

Обратите внимание, что мне нужно решение, которое работает на платформе Linux и должно использовать pthreads. Меня не волнует ни одна другая платформа.

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

Я прочитал некоторые справочные страницы о политиках планирования и приоритетах планирования в Linux, но не могу разобрать: (

Будет ли это работать? Можете ли вы помочь мне с точным API-интерфейсом pthread, необходимым для выполнения вышеуказанной задачи?

С уважением Лали

Ответы [ 6 ]

11 голосов
/ 19 июля 2010

Вот моя реализация.Потоки с низким приоритетом используют prio_lock_low() и prio_unlock_low() для блокировки и разблокировки, потоки с высоким приоритетом используют prio_lock_high() и prio_unlock_high().

. Дизайн довольно прост.Потоки с высоким приоритетом хранятся в критической секции mutex ->cs_mutex, а потоки с низким приоритетом - в переменной условия.Условная переменная mutex хранится только вокруг обновлений совместно используемой переменной и сигнализации условной переменной.

#include <pthread.h>

typedef struct prio_lock {
    pthread_cond_t cond;
    pthread_mutex_t cv_mutex; /* Condition variable mutex */
    pthread_mutex_t cs_mutex; /* Critical section mutex */
    unsigned long high_waiters;
} prio_lock_t;

#define PRIO_LOCK_INITIALIZER { PTHREAD_COND_INITIALIZER, PTHREAD_MUTEX_INITIALIZER, PTHREAD_MUTEX_INITIALIZER }

void prio_lock_low(prio_lock_t *prio_lock)
{
    pthread_mutex_lock(&prio_lock->cv_mutex);
    while (prio_lock->high_waiters || pthread_mutex_trylock(&prio_lock->cs_mutex))
    {
        pthread_cond_wait(&prio_lock->cond, &prio_lock->cv_mutex);
    }
    pthread_mutex_unlock(&prio_lock->cv_mutex);
}

void prio_unlock_low(prio_lock_t *prio_lock)
{
    pthread_mutex_unlock(&prio_lock->cs_mutex);

    pthread_mutex_lock(&prio_lock->cv_mutex);
    if (!prio_lock->high_waiters)
        pthread_cond_signal(&prio_lock->cond);
    pthread_mutex_unlock(&prio_lock->cv_mutex);
}

void prio_lock_high(prio_lock_t *prio_lock)
{
    pthread_mutex_lock(&prio_lock->cv_mutex);
    prio_lock->high_waiters++;
    pthread_mutex_unlock(&prio_lock->cv_mutex);

    pthread_mutex_lock(&prio_lock->cs_mutex);
}

void prio_unlock_high(prio_lock_t *prio_lock)
{
    pthread_mutex_unlock(&prio_lock->cs_mutex);

    pthread_mutex_lock(&prio_lock->cv_mutex);
    prio_lock->high_waiters--;
    if (!prio_lock->high_waiters)
        pthread_cond_signal(&prio_lock->cond);
    pthread_mutex_unlock(&prio_lock->cv_mutex);
}
6 голосов
/ 19 июля 2010

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

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

С точки зрения концепций и API, которые вам нужны, это относительно похоже на реализацию блокировки чтения / записи (но семантика, которая вам нужна, очевидно, совершенно иная - но если вы поняли, как работает блокировка r / w , вы поймете, как реализовать то, что вы хотите).

Вы можете увидеть реализацию блокировки чтения-записи здесь:

http://ptgmedia.pearsoncmg.com/images/0201633922/sourcecode/rwlock.c

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

(Книга, из которой взят код, приведенный выше, также является прекрасной книгой рассылок posix, http://www.informit.com/store/product.aspx?isbn=0201633922)

2 голосов
/ 11 августа 2012

Собственный способ - включить наследование приоритетов для вашего мьютекса (с помощью pthread_mutex_attr) и использовать приоритет потока pthread, чтобы выполнить то, что вам нужно. Требуется всего несколько строк кода, и вы не изобретаете колесо. С другой стороны, он также будет работать с планировщиком RT или FIFO, в то время как ваша домашняя версия не будет.

Затем, всякий раз, когда поток с высоким приоритетом ожидает мьютекс, полученный потоком с более низким приоритетом, ядро ​​«увеличивает» поток с низким приоритетом, чтобы его можно было запланировать вместо потока с высоким приоритетом, тем самым давая ему временная квитанция, чтобы снять блокировку. Как только блокировка снята, запланирован поток с высоким приоритетом. Это самая низкая задержка, которую вы можете получить, поскольку она выполняется в ядре.

2 голосов
/ 19 июля 2010

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

РЕДАКТИРОВАТЬ (спасибо JosephH)

семафор exec, установленный в 3 (количество потоков с высоким уровнем prio), отмечает, что pend(exec,3); означает, что этот режим ожидания будет находиться в спящем режиме, пока не будут доступны все 3 слота, и будет использовать их все



//init
exec = semaphore(3,3);

//========================

if this is NOT thread (t1,t2,t3)
    lock(low_prio);
    sem_pend(exec,3);
else
    sem_pend(exec,1);
lock(high_prio);
//...
unlock(high_prio);
if this is NOT thread (t1,t2,t3)
    sem_release(exec,3);
    sleep(0); //yield();  //ensures that sem_pend(exec,1) is executed
    unlock(low_prio);
else
    sem_release(exec,1);
0 голосов
/ 19 июля 2010

Для реализации этого с pthreads вам понадобится N списков, по одному на приоритет потока.Списки будут содержать указатели на переменные потока pthread_cond_t.

Схема непроверенного мета-кода:

/* the main lock */
pthread_mutex_t TheLock = PTHREAD_MUTEX_INITIALIZER;

/* service structures: prio lists and the lock for them */
pthread_mutex_t prio_list_guard = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t *prio_lists[MY_MAX_PRIO][MY_MAX_THREAD]; /* 0 == highest prio */

/* lock */
void
prio_lock(int myprio)
{
    pthread_cond_t x;

    pthread_mutex_lock( &prio_list_guard );

    if (0 == pthread_mutex_trylock( &TheLock )) {
        pthread_mutex_unlock( &prio_list_guard );
        return 0;
    }

    pthread_cond_init( &x, 0 );
    LIST_ADD( prio_lists[myprio], &x )

    while(1)    /* handle spurious wake-ups */
    {
        pthread_cond_wait( &prio_list_guard, &x );
        if (0 == pthread_mutex_trylock( &TheLock )) 
        {
            LIST_REMOVE( prio_lists[myprio], &x );
            pthread_mutex_unlock( &prio_list_guard );
            return 0;
        }
    }
}

/* unlock */
void
prio_unlock()
{
    int i;
    pthread_cond_t *p;

    pthread_mutex_lock( &prio_list_guard );

    for (i=0; i<MY_MAX_PRIO; i++)
    {
        if ((p = LIST_GETFIRST( prio_lists[i] )))
        {
            pthread_cond_signal( p );
            break;
        }
    }

    pthread_mutex_unlock( &TheLock );

    pthread_mutex_unlock( &prio_list_guard );
}

Код также обрабатывает ложные пробуждения из pthread_cond_wait(), но, честно говоря, я никогдавидел, что происходит.

Edit1.Обратите внимание, что prio_lists выше - это примитивная форма очереди приоритетов .

0 голосов
/ 19 июля 2010

(первые две попытки имели ошибки, просьба перейти к редактированию 2)

Может быть, это сработает?

if NOT this thread is T1 or T2 or T3
    pthread_mutex_lock(&lock1) // see note below
    pthread_mutex_lock(&lock2)
    Some trivial code
    pthread_mutex_unlock(&lock2)
    pthread_mutex_unlock(&lock1)
else
    pthread_mutex_lock(&lock2)
    Some trivial code
    pthread_mutex_unlock(&lock2)        
end if

Рассуждение: Некоторые потоки будут конкурировать за две блокировки и, следовательно, будут иметь более низкий приоритет, а некоторые потоки будут конкурировать только за одну блокировку и, следовательно, будут иметь более высокий приоритет. Тем не менее, разница может быть незначительной, и тогда было бы разрешить ввести некоторое отставание между получением первой блокировки и попыткой второй блокировки для потоков с более высоким приоритетом, во время которого потокам с более высоким приоритетом будет предоставлена ​​возможность получить блокировку 2.
(отказ от ответственности: я новичок, когда дело доходит до этого)

EDIT: Еще одна попытка / подход

if NOT (this thread is T1 or T2 or T3)  
    pthread_mutex_lock(&lock1)
    if pthread_mutex_trylock(&lock2) == 0  // low priority threads will not get queued
        Some trivial code
        pthread_mutex_unlock(&lock2)
    end if
    pthread_mutex_unlock(&lock1)
else 
    if (this thread is T1 or T2 or T3)
        pthread_mutex_lock(&lock2)
        Some trivial code
        pthread_mutex_unlock(&lock2)        
    end if
end if

РЕДАКТИРОВАТЬ 2: Еще одна попытка (попытка узнать что-то здесь)

if NOT (this thread is T1 or T2 or T3)  
    pthread_mutex_lock(&lock1)
    while !(pthread_mutex_trylock(&lock2) == 0)
        pthread_yield()
    Some trivial code
    pthread_mutex_unlock(&lock2)
    pthread_mutex_unlock(&lock1)
else 
    if (this thread is T1 or T2 or T3)
        pthread_mutex_lock(&lock2)
        Some trivial code
        pthread_mutex_unlock(&lock2)        
    end if
end if
...