2010-07-19 15 views
12

Supongamos que el siguiente código se está ejecutando mediante 10 subprocesos.Cómo permitir que ciertos subprocesos tengan prioridad para bloquear un uso de mutex PTHREADS

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

fines de explicaciones digamos que los hilos son T1, T2, T3 ..... T10. Mi requisito es que mientras T1 o T2 o T3 (es decir, cualquiera de T1, T2 o T3) esté esperando la adquisición de un bloqueo, el otro lo enrosque T4, T5, T6 ..... T10 no debería ser capaz de adquirir el bloqueo, es decir, T1, T2 y T3 deben tener prioridad para adquirir el bloqueo con respecto a otros hilos.

supongo que podría hacerse aumentando la prioridad de hilos T1, T2 y T3

es decir, aquí está el código de pseudo

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 

Tenga en cuenta que yo quiero una solución que sea obras para Plataforma Linux y debería usar pthreads. Realmente no me importa ninguna otra plataforma.

También tenga en cuenta que realmente no quiero hacer estos 3 hilos en tiempo real, quiero que muestren su comportamiento definitivo (programación y prioridad), salvo que en el pequeño fragmento de código antes mencionado quiero que siempre tener prioridad en la adquisición de bloqueo.

He leído algunas páginas del manual sobre políticas de planificación y prioridades de programación en Linux, pero no puedo distinguir :(

es que esto funciona? ¿Me puede ayudar con la API pthread exacta requerida para realizar la tarea anterior ?

Saludos lali

+0

considere agregar la etiqueta 'c' ya que solo unos pocos desarrolladores están viendo las preguntas sin una etiqueta de lenguaje de programación. – Dummy00001

+0

muchas gracias, sí, es similar a la implementación de bloqueos de lectura y escritura. Gracias a todos :) – ghayalcoder

Respuesta

5

Según tengo entendido, la única manera de que pueda garantizar esto sería escribiendo un candado que funcione así.

Necesitará variables de condición y conteos de la cantidad de hilos de baja/alta prioridad en espera.

En términos de los conceptos y API que necesitará, es relativamente similar a implementar un bloqueo de lectura/escritura (pero la semántica que necesita es completamente diferente, obviamente, pero si entendió cómo es el bloqueo de r/w trabajando, comprenderá cómo implementar lo que quiere).

Se puede ver una implementación de un bloqueo de lectura escritura aquí:

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

En los hilos de menor prioridad, que había necesidad de esperar a que los hilos de alta prioridad para terminar, de la misma manera los lectores esperan para que los escritores terminen

(El libro del código anterior es tomada de ella también una gran hilos POSIX reservar por cierto, http://www.informit.com/store/product.aspx?isbn=0201633922)

2

Alternativamente es posible que sólo introducir otro bloqueo para roscas de mayor prioridad. considere el siguiente pseudo-código (no estoy familiarizado con la semántica pthread, pero creo que esto no es difícil para mapear la codificar a las llamadas necesarias)

EDIT (Gracias JosephH)

introducir el semáforo exec ajustado a 3 (número de hilos de alto prio) nota que pend(exec,3); significa que este pend dormirá hasta que las 3 ranuras están disponibles y se les consumir todo



//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

Lo siento, pero realmente no veo cómo funciona eso. Si tanto t1 como t4 intentan tomar la cerradura mientras t2 la tiene, entonces t1 y t4 terminarán esperando en la cerradura (high_prio) y no se garantizará cuál se despertará primero. – JosephH

+0

yepp, tienes razón, esto me he perdido. Aún así, estoy seguro de que hay una manera, considere la edición ... – ULysses

+0

No creo que esto funcione tampoco; el enfoque de dos bloqueos podría funcionar, pero tendría que dar a los subprocesos de "alta prioridad" la posibilidad de superar a los de "baja prioridad" (por lo que debería implementarse como un bloqueo de dos pasos). – Unreason

0

(Los dos primeros intentos tuvieron errores, pls saltar a Edit2)

Tal vez esto funcionaría?

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 

Razonamiento: Algunos hilos competirán por dos cerraduras y por lo tanto tienen una menor prioridad y algunos hilos competirán por una sola cerradura y por lo tanto tendrán mayor prioridad. Aún así, la diferencia puede ser marginal y la resolución sería introducir algún retraso entre adquirir el primer bloqueo e intentar el segundo bloqueo para los subprocesos de mayor prioridad, en cuyo momento los subprocesos de mayor prioridad tendrán la oportunidad de obtener el bloqueo2.
(disclaimer: Yo soy muy novato cuando se trata de esto)

EDIT: Otro intento/enfoque

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: Otro intento (intentar aprender algo aquí)

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 
+0

sospecho que quería probar si un hilo 'NO es t1 o t2 o t3', es decir, baja prioridad. este enfoque tiene un tipo de falla que tenía mi primera versión: si el hilo hp se está ejecutando, entonces podría haber otros dos hilos hp y lp esperando en el bloqueo2, y no se puede saber quién lo adquirirá primero – ULysses

+0

@ULysses, sí NO fue pensado (editado). En cuanto a los varios hilos esperando por lock2, creo que tienes razón. Menciono la introducción del retraso, pero parece que no es suficiente para garantizar la prioridad y si entiendo correctamente su enfoque de semáforo lo garantizaría. Pondré otra edición ... – Unreason

+0

ahora lo que tienes es una caída del hilo lp, aunque en caso de que se esté ejecutando un hilo hp – ULysses

0

Para implementar eso con pthreads necesitaría N listas, una por prioridad de subproceso. Las listas contendrían punteros a las variables pthread_cond_t del subproceso.

Esquema no probado meta-código:

/* 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); 
} 

El código también se encarga de espurios despertares de pthread_cond_wait(), pero, francamente, nunca he visto que eso ocurra.

Edit1. Tenga en cuenta que prio_lists arriba es una forma primitiva de priority queue.

+0

meta código no debe significar código sucio.en 'prio_lock()' bloquea 'prio_list_guard' dos veces, en' prio_unlock() 'primero debe desbloquear' TheLock', y luego señalizar la lista; de lo contrario, siempre tendrá un despertar 'espúreo' en un ciclo – ULysses

+0

@ULysses: arreglado. Para el "primer desbloqueo de TheLock": puedes hacerlo, pero de esa manera es IMO más limpio. Y es una cuestión de gusto. 'man pthread_cond_wait' para el bloqueo implícito del mutex condicional. El hilo de bloqueo se activará solo después de que el hilo de desbloqueo suelte el bloqueo prio_list_guard. – Dummy00001

+0

@Dummy, OK, no tenía experiencia con la pthread lib. Este código ahora se ve bien para mí. – ULysses

10

Aquí está mi implementación. Los subprocesos de prioridad baja usan prio_lock_low() y prio_unlock_low() para bloquear y desbloquear, los subprocesos de alta prioridad usan prio_lock_high() y prio_unlock_high().

El diseño es bastante simple. Los subprocesos de alta prioridad se mantienen en la sección crítica mutex ->cs_mutex, los subprocesos de baja prioridad se mantienen en la variable de condición. La variable de condición mutex solo se mantiene alrededor de las actualizaciones de la variable compartida y la señalización de la variable de condición.

#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); 
} 
+0

Esto debería funcionar, pero es muy detallado comparando con la misma lógica que se logró con el semáforo – ULysses

+0

@ ULisses: Supongo, pero al menos solo usa primitivas pthreads y estaría oculto en un módulo de implementación; el código que lo usa simplemente haría 'prio_lock _ * (&lock);/* ... */prio_unlock _ * (&lock);) alrededor de su sección crítica. – caf

0

La forma nativa es permitir que la herencia de prioridad para su exclusión mutua (con pthread_mutex_attr), y el uso de hilo de prioridad pthread para llevar a cabo lo que necesita. Solo requiere muy pocas líneas de código, y no está reinventando la rueda. Por el lado bueno, también funcionará con el programador RT o FIFO, mientras que su versión casera no funcionará.

Entonces, cada vez que un hilo con una alta prioridad espera en un mutex que ha adquirido por un hilo de menor prioridad, el núcleo de "reforzar" el hilo de baja prioridad para que pueda ser programada en lugar del hilo de alta prioridad, dando así es un intervalo de tiempo para liberar el bloqueo. Tan pronto como se libera el bloqueo, se programa el hilo de alta prioridad. Esa es la demora más baja que puede obtener desde que se hace en el kernel.

Cuestiones relacionadas