Подтвердить что ты не робот

Как разрешить определенным потокам иметь приоритет при блокировке использования мьютекса PTHREADS

Предположим, что следующий код выполняется 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 потока выполнялись как в реальном времени, я хочу, чтобы они демонстрировали свое поведение defualt (планирование и приоритет), за исключением того, что в вышеупомянутом небольшом фрагменте кода я хочу, чтобы они всегда имели приоритет при получении блокировки.

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

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

Отношения Lali

4b9b3361

Ответ 1

Как я понимаю, единственным способом, которым вы действительно можете это гарантировать, было бы написать блокировку, которая работает именно так.

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

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

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

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

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

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

Ответ 2

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

Дизайн довольно прост. Высокоприоритетные потоки хранятся в мьютексе критического раздела ->cs_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);
}

Ответ 3

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

EDIT (thanx JosephH)

введение семафора exec в 3 (количество потоков с высоким приоритетом) обратите внимание, что 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);

Ответ 4

(Первые две попытки имели ошибки, pls перешли на EDIT2)

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

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

EDIT2: Еще одна попытка (что-то узнать здесь)

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

Ответ 5

Чтобы реализовать это с помощью 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 выше является примитивной формой очереди .

Ответ 6

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

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