Предположите, что следующий код выполняется 10 потоками.
pthread_mutex_lock(&lock)
Some trivial code
pthread_mutex_unlock(&lock)
Ради объяснений позволяет, говорят, что потоки являются T1, T2, T3..... T10. Мое требование - то, что, пока T1 или T2 или T3 (т.е. любой из T1, T2 или T3) ожидают получения блокировки, другого i.t 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, но не могу действительно разобрать :(
Это будет работать? Можно ли помочь мне с точным pthread API, требуемым выполнить вышеупомянутую задачу?
Отношения lali
Насколько я понимаю, единственный способ действительно гарантировать это - это написать блокировку, которая работает так же самостоятельно. Однако ответ @ xryl669 , в котором предлагается использовать приоритет потока и наследование приоритетов, безусловно, заслуживает рассмотрения, если он работает для вашего варианта использования.
Чтобы реализовать это самостоятельно, вам потребуются условные переменные и счетчики количества ожидающих потоков с низким / высоким приоритетом.
С точки зрения концепций и API, которые вам понадобятся, это относительно похоже на реализацию блокировки чтения / записи (но семантика, которая вам нужна, очевидно, совершенно другая, но если вы поняли, как работает блокировка чтения / записи , вы поймете, как реализовать то, что хотите).
Вы можете увидеть реализацию блокировки чтения и записи здесь:
http://ptgmedia.pearsoncmg.com/images/0201633922/sourcecode/rwlock.c
В потоках с более низким приоритетом вам понадобится чтобы дождаться завершения высокоприоритетных потоков, точно так же читатели ждут завершения писателей.
(Книга с указанным выше кодом взята из нее, кстати, http://www.informit.com/store/product.aspx?isbn=0201633922 )
В качестве альтернативы вы можете просто ввести еще одну блокировку для потоков с более высоким приоритетом. рассмотрим следующий псевдокод (я не знаком с семантикой pthread, но считаю, что сопоставить код с необходимыми вызовами несложно)
EDIT (thanx JosephH)
, вводя семафор exec, установленный на 3 (количество потоков с высокой приоритетностью)
обратите внимание, что pend (exec, 3);
означает, что этот pend будет бездействовать до тех пор, пока все 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);
(Первые две попытки имели ошибки, пожалуйста, перейдите к 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.
(отказ от ответственности: я новичок, когда дело доходит до этого)
РЕДАКТИРОВАТЬ: Другая попытка / подход
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
Чтобы реализовать это с помощью 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
выше является примитивной формой очереди приоритетов .
Вот моя реализация. Потоки с низким приоритетом используют 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);
}