Komplexe Synchronisationsobjekte erstellen
Die in der Threadbibliothek bereitgestellten Subroutinen können als Basiselemente verwendet werden, um komplexere Synchronisationsobjekte zu erstellen.
Lange Sperren
Die von der Threadbibliothek bereitgestellten Mutexe sind Objekte mit geringer Konkurrenzsituation und sollten nicht sehr lange gehalten werden. Lange Sperren werden mit Mutexen und Bedingungsvariablen implementiert, sodass eine lange Sperre lange gehalten werden kann, ohne die Leistung des Programms zu beeinträchtigen. Lange Sperren sollten nicht verwendet werden, wenn die Stornierbarkeit aktiviert ist.
Eine lange Sperre hat den Datentyp long_lock_t . Sie muss von der Routine long_lock_init initialisiert werden. Die Subroutine long_lock, long_trylockund long_unlock führt ähnliche Operationen wie die pthread_mutex_lockaus. Subroutine pthread_mutex_trylockund pthread_mutex_unlock
typedef struct {
pthread_mutex_t lock;
pthread_cond_t cond;
int free;
int wanted;
} long_lock_t;
void long_lock_init(long_lock_t *ll)
{
pthread_mutex_init(&ll->lock, NULL);
pthread_cond_init(&ll->cond);
ll->free = 1;
ll->wanted = 0;
}
void long_lock_destroy(long_lock_t *ll)
{
pthread_mutex_destroy(&ll->lock);
pthread_cond_destroy(&ll->cond);
}
void long_lock(long_lock_t *ll)
{
pthread_mutex_lock(&ll->lock);
ll->wanted++;
while(!ll->free)
pthread_cond_wait(&ll->cond);
ll->wanted--;
ll->free = 0;
pthread_mutex_unlock(&ll->lock);
}
int long_trylock(long_lock_t *ll)
{
int got_the_lock;
pthread_mutex_lock(&ll->lock);
got_the_lock = ll->free;
if (got_the_lock)
ll->free = 0;
pthread_mutex_unlock(&ll->lock);
return got_the_lock;
}
void long_unlock(long_lock_t *ll)
{
pthread_mutex_lock(&ll->lock);
ll->free = 1;
if (ll->wanted)
pthread_cond_signal(&ll->cond);
pthread_mutex_unlock(&ll->lock);
}
Semaphoren
Traditionelle Semaphoren in UNIX-Systemen sind Einrichtungen zur Synchronisierung zwischen Prozessen. Für eine bestimmte Verwendung können Sie Interthread-Semaphore implementieren.
Ein Semaphor hat den Datentyp sema_t . Sie muss von der Routine sema_init initialisiert und mit der Routine sema_destroy gelöscht werden. Die Semaphorwarteoperationen und Semaphornachoperationen werden jeweils von den Routinen sema_p und sema_v ausgeführt.
typedef struct {
pthread_mutex_t lock;
pthread_cond_t cond;
int count;
} sema_t;
void sema_init(sema_t *sem)
{
pthread_mutex_init(&sem->lock, NULL);
pthread_cond_init(&sem->cond, NULL);
sem->count = 1;
}
void sema_destroy(sema_t *sem)
{
pthread_mutex_destroy(&sem->lock);
pthread_cond_destroy(&sem->cond);
}
void p_operation_cleanup(void *arg)
{
sema_t *sem;
sem = (sema_t *)arg;
pthread_mutex_unlock(&sem->lock);
}
void sema_p(sema_t *sem)
{
pthread_mutex_lock(&sem->lock);
pthread_cleanup_push(p_operation_cleanup, sem);
while (sem->count <= 0)
pthread_cond_wait(&sem->cond, &sem->lock);
sem->count--;
/*
* Note that the pthread_cleanup_pop subroutine will
* execute the p_operation_cleanup routine
*/
pthread_cleanup_pop(1);
}
void sema_v(sema_t *sem)
{
pthread_mutex_lock(&sem->lock);
if (sem->count <=0)
pthread_cond_signal(&sem->cond);
sem->count++;
pthread_mutex_unlock(&sem->lock);
}
Der Zähler gibt die Anzahl der Benutzer an, die das Semaphor verwenden dürfen. Sie ist nie streng negativ; daher gibt sie nicht die Anzahl der wartenden Benutzer an, wie bei traditionellen Semaphoren. Diese Implementierung stellt eine typische Lösung für das Problem der Mehrfachaktivierung in der Subroutine pthread_cond_wait dar. Die Semaphorwarteoperation kann abgebrochen werden, weil die Subroutine pthread_cond_wait einen Abbruchpunkt bereitstellt.
Schreib-/Schreibsperren mit Priorität
Eine Lese-/Schreibsperre mit Schreibpriorität bietet mehrere Threads mit gleichzeitigem Lesezugriff auf eine geschützte Ressource und einen Einzelthread mit Schreibzugriff auf die Ressource unter Ausschluss von Lesevorgängen. Wenn ein Writer eine Sperre freigibt, erhalten andere wartende Writer die Sperre vor einem wartenden Reader. Lese-/Schreibsperren mit Schreibpriorität werden normalerweise verwendet, um Ressourcen zu schützen, die häufiger gelesen als geschrieben werden.
Eine Lese-/Schreibsperre mit Schreibpriorität hat den Datentyp rwlock_t . Sie muss von der Routine rwlock_init initialisiert werden. Die Routine rwlock_lock_read sperrt die Sperre für einen Leser (mehrere Leser sind zulässig), die Routine rwlock_unlock_read entsperrt sie. Die Routine rwlock_lock_write sperrt die Sperre für einen Writer, die Routine rwlock_unlock_write entsperrt sie. Die richtige Entsperrroutine (für das Eingabeprogramm oder für das Ausgabeprogramm) muss aufgerufen werden.
typedef struct {
pthread_mutex_t lock;
pthread_cond_t rcond;
pthread_cond_t wcond;
int lock_count; /* < 0 .. held by writer */
/* > 0 .. held by lock_count readers */
/* = 0 .. held by nobody */
int waiting_writers; /* count of wating writers */
} rwlock_t;
void rwlock_init(rwlock_t *rwl)
{
pthread_mutex_init(&rwl->lock, NULL);
pthread_cond_init(&rwl->wcond, NULL);
pthread_cond_init(&rwl->rcond, NULL);
rwl->lock_count = 0;
rwl->waiting_writers = 0;
}
void waiting_reader_cleanup(void *arg)
{
rwlock_t *rwl;
rwl = (rwlock_t *)arg;
pthread_mutex_unlock(&rwl->lock);
}
void rwlock_lock_read(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
pthread_cleanup_push(waiting_reader_cleanup, rwl);
while ((rwl->lock_count < 0) && (rwl->waiting_writers))
pthread_cond_wait(&rwl->rcond, &rwl->lock);
rwl->lock_count++;
/*
* Note that the pthread_cleanup_pop subroutine will
* execute the waiting_reader_cleanup routine
*/
pthread_cleanup_pop(1);
}
void rwlock_unlock_read(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
rwl->lock_count--;
if (!rwl->lock_count)
pthread_cond_signal(&rwl->wcond);
pthread_mutex_unlock(&rwl->lock);
}
void waiting_writer_cleanup(void *arg)
{
rwlock_t *rwl;
rwl = (rwlock_t *)arg;
rwl->waiting_writers--;
if ((!rwl->waiting_writers) && (rwl->lock_count >= 0))
/*
* This only happens if we have been canceled
*/
pthread_cond_broadcast(&rwl->wcond);
pthread_mutex_unlock(&rwl->lock);
}
void rwlock_lock_write(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
rwl->waiting_writers++;
pthread_cleanup_push(waiting_writer_cleanup, rwl);
while (rwl->lock_count)
pthread_cond_wait(&rwl->wcond, &rwl->lock);
rwl->lock_count = -1;
/*
* Note that the pthread_cleanup_pop subroutine will
* execute the waiting_writer_cleanup routine
*/
pthread_cleanup_pop(1);
}
void rwlock_unlock_write(rwlock_t *rwl)
{
pthread_mutex_lock(&rwl->lock);
l->lock_count = 0;
if (!rwl->wating_writers)
pthread_cond_broadcast(&rwl->rcond);
else
pthread_cond_signal(&rwl->wcond);
pthread_mutex_unlock(&rwl->lock);
}
Leser werden nur gezählt. Wenn die Anzahl null erreicht, kann ein wartender Writer die Sperre übernehmen. Nur ein Ausgabeprogramm kann die Sperre halten. Wenn die Sperre von einem Writer freigegeben wird, wird ein anderer Writer aktiviert, wenn es einen gibt. Andernfalls werden alle wartenden Leser geweckt.
Die Sperrroutinen können abgebrochen werden, weil sie die Subroutine pthread_cond_wait aufrufen. Bereinigungsroutinen werden daher registriert, bevor die Subroutine aufgerufen wird.