Notas de função comumente usadas de multithreading Linux
Uso da função thread
[Tipo de TID: pthread_t]
- pthread_t é um tipo de dados estruturado, portanto, as implementações de sistemas operacionais portáteis não podem tratá-lo como um número inteiro
- Linux 3.2.0 usa um inteiro longo sem sinal para representar o tipo de dados pthread_t
[Comparação do tópico TID: pthread_equal]
#include <pthread.h> int pthread_equal(pthread_t tid1, pthread_t tid2); // 若相等,返回非0数值;否则,返回0
[Obtenha seu próprio ID de tópico: pthread_self]
- Ao imprimir com printf, use "% lu" para imprimir o valor do tipo pthread_t
#include <pthread.h> pthread_t pthread_self(void); // 返回调用线程的线程ID
[Gettid () para obter o título do tópico]
- A função gettid () pode obter o tid do thread, mas a glibc não implementa esta função, ela só pode ser obtida através da chamada do sistema Linux syscall (<sys / syscall.h>)
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/syscall.h> void *hello() { printf("child,the tid=%lu,pid=%ld\n",pthread_self(),syscall(SYS_gettid)); } int main(int argc,char *agrv[]) { pthread_t thread_id; pthread_create(&thread_id,NULL,(void *)*hello,NULL); printf("parent,the tid=%lu,pid=%ld\n",pthread_self(),syscall(SYS_gettid)); pthread_join(thread_id,NULL); }
- Observação:
- Use o método acima para verificar: o PID do thread principal é o mesmo que o PID do processo
- Quando nenhum encadeamento é criado no processo, o processo principal é equivalente a um encadeamento principal, porque o ID do processo e o encadeamento são os mesmos
[Função de criação de thread: pthread_create]
#include <pthread.h> int pthread_create( pthread_t *restrict tidp, //当pthread_create成功返回时,新创建的线程ID会被设置到tidp所指向的内存单元中 const pthread_attr_t *restrict attr, //atrr参数用于指定线程创建时的初始化属性。值为NULL时,创建一个具有默认属性的线程。 void *(*start_rtn)(void *), //新创建的线程从start_rtn函数的地址开始运行。该函数只有一个无类型指针参数arg,返回值为void*类型 void *restrict arg //如果需要向start_rtn函数传递的参数有一个以上,需要把这些参数放到一个结构中,传递该结构的地址 );
- valor de retorno:
- Sucesso: retorno 0
- Erro: retorna o número do erro sem definir errno
- Caso 1
#include<pthread.h> #include<stdio.h> #include<stdlib.h> pthread_t ntid; void printids(const char *s){ pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf("%s pid %lu tid %lu (0x%lx)\n", s, (unsigned long)pid,(unsigned long)tid, (unsigned long)tid); } void *thr_fn(void *arg){ printids("new thread: "); return((void *)0); } int main(void) { int err; //create thread err = pthread_create(&ntid, NULL, thr_fn, NULL); if (err != 0) printf("can’t create thread\n"); printids("main thread:"); sleep(1); exit(0); }
- Caso 2
#include <pthread.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/syscall.h> struct message { int i; int j; }; void *hello(struct message *str) { printf("child,the tid=%lu,pid=%ld\n",pthread_self(),syscall(SYS_gettid)); printf("the arg.i is %d,arg.j is %d\n",str->i,str->j); } int main(int argc,char *agrv[]) { struct message test; pthread_t thread_id; test.i=10; test.j=20; pthread_create(&thread_id, NULL,(void *)*hello,&test); printf("parent,the tid=%lu,pid=%ld\n",pthread_self(),syscall(SYS_gettid)); pthread_join(thread_id,NULL); }
[Estrutura de atributos do tópico: pthread_attr_t]
- O conteúdo dessa estrutura contém o valor do atributo do encadeamento e o parâmetro 2 de pthread_create usa essa estrutura para definir o valor do atributo inicial para o encadeamento.
[Criação e destruição da estrutura de atributos do thread]
int pthread_attr_init(pthread_attr_t *attr);
- Função: usada para inicializar o valor do atributo do thread
- Nota: Depois de chamar esta função, os atributos da estrutura de atributos são valores padrão do sistema.Se você deseja definir outros atributos, você precisa chamar funções diferentes para defini-los.
int pthread_attr_destroy(pthread_attr_t *attr);
- Função: usada para inicializar (destruir) atributos. Se os atributos do thread inicializados pela função pthread_attr_init forem alocados dinamicamente, então esta função irá liberar o espaço de memória dos atributos do thread
- A função pthread_attr_destory inicializa o objeto de atributo com um valor inválido, portanto, após desinicializar o atributo com esta função, o atributo não pode ser usado pela função pthread_create.
[Método de terminação de thread]
- Os três métodos a seguir são para encerrar o encadeamento e interromper seu fluxo de controle sem encerrar todo o processo normalmente:
- ① O encadeamento pode simplesmente retornar da rotina de inicialização e o valor de retorno é o código de saída do encadeamento
- ② O thread pode ser cancelado por outros threads no mesmo processo ( pthread_cancel )
- ③Thread chama pthread_exit
[Função de saída do tópico: pthread_exit]
#include <pthread.h> void pthread_exit(void *rval_ptr);
- Função: o thread chama esta função para se encerrar
- Parâmetro: rval_ptr é um ponteiro sem tipo e o conteúdo pode ser definido neste ponteiro para sair do processo (geralmente para definir o código de finalização).
- Outros threads no processo também podem acessar este ponteiro chamando a função pthread_join
#include<pthread.h> #include<stdlib.h> #include<stdio.h> void * thr_fn1(void *arg){ printf("thread 1 returning\n"); return((void *)1); } void *thr_fn2(void *arg){ printf("thread 2 exiting\n"); pthread_exit((void *)2); } int main(void) { int err; pthread_t tid1, tid2; void *tret; err = pthread_create(&tid1, NULL, thr_fn1, NULL);//创建线程1 if (err != 0) printf("can’t create thread 1\n"); err = pthread_create(&tid2, NULL, thr_fn2, NULL);//创建线程2 if (err != 0) printf("can’t create thread 2\n"); err = pthread_join(tid1, &tret);//等待线程1 if (err != 0) printf("can’t join with thread 1\n"); printf("thread 1 exit code %ld\n", (long)tret); err = pthread_join(tid2, &tret);//等待线程2 if (err != 0) printf("can’t join with thread 2\n"); printf("thread 2 exit code %ld\n", (long)tret); exit(0); }
[Função de espera de thread pthread_join]
#include <pthread.h> int pthread_join(pthread_t thread, void **rval_ptr);
- Valor de retorno: retornar 0 em caso de sucesso; retornar o número do erro em caso de falha
- Função: usada para esperar o fim do thread especificado pelo parâmetro 1
- Esta função irá bloquear até que a thread especificada chame pthread_exit, retorne da thread inicial ou seja cancelada, então esta função retorna
- parâmetro:
- Parâmetro 1: especifique o ID do segmento de espera
- Parâmetro 2:
- Preencher NULL: Obtenha as informações de encerramento do thread em espera. Se você não estiver interessado nas informações de encerramento do thread, poderá defini-lo como NULL.
- Não vazio: Se a thread simplesmente retornar de sua rotina de retorno, rval_ptr conterá o código de retorno.
- Se o thread for cancelado, a unidade de memória especificada por rval_ptr é definida como PTHREAD_CANCELED
- A relação entre o uso da função thread_join e separação de thread:
- Chame pthread_join para esperar por um thread. Após o término do thread em espera, ele será colocado em um estado separado, para que os recursos usados pelo thread possam ser restaurados
- Se pthread_join for chamado para esperar por um thread, se o thread já estiver em um estado separado (por exemplo, a função pthread_detach é chamada), a chamada de pthread_join falhará e retornará EINVAL, embora este comportamento esteja relacionado à implementação específica
[Função de cancelamento de thread: pthread_cancel]
#include <pthread.h> int pthread_cancel(pthread_t tid);
- Valor de retorno: retorna 0 se for bem sucedido; caso contrário, retorna o número do erro
- Função: Thread pode solicitar o cancelamento de outros threads no mesmo processo por meio de pthread_cancel
- pthread_cancel não espera o encadeamento terminar, ele apenas faz uma solicitação
- parâmetro:
- ID da discussão que precisa ser cancelada
- Precauções:
- Por padrão, a função pthread_cancel fará com que a thread identificada por tid se comporte como se estivesse chamando a função pthread_exit com o parâmetro PTHREAD_CANCELED, mas a thread cancelada pode escolher ignorar o cancelamento ou controlar como ela é cancelada
[Ponto de cancelamento do tópico]
- Conceito: O sistema personaliza alguns pontos de cancelamento de thread. Quando um encadeamento recebe uma solicitação de cancelamento de outro encadeamento, se não foi executado até o ponto de cancelamento, o encadeamento continuará a ser executado até chegar a um determinado ponto de cancelamento, o encadeamento é realmente cancelado. A tecnologia de ponto de cancelamento de encadeamento também é chamada "adiamento". cancelar "
#include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <unistd.h> void *thread_func(void* arg); int main() { pthread_t tid; int *thread_exit_code=malloc(sizeof(int)); if(pthread_create(&tid,NULL,thread_func,NULL)!=0){ fprintf(stdout,"pthread_create error\n"); exit(EXIT_FAILURE); } //进程休眠1秒,然后取消子线程 sleep(1); if(pthread_cancel(tid)!=0){ fprintf(stdout,"pthread_cancel error\n"); exit(EXIT_FAILURE); } printf("pthread_cancel filaed\n"); //睡眠8秒之后取消线程失败了,因为线程已经退出了 sleep(8); if(pthread_cancel(tid)!=0){ fprintf(stdout,"pthread_cancel error\n"); exit(EXIT_FAILURE); } printf("kill thread success\n"); if(pthread_join(tid,(void*)&thread_exit_code)==0){ printf("pthread_join success,exit_code is %d\n",(int)*thread_exit_code); }else{ fprintf(stdout,"pthread_join error\n"); exit(EXIT_FAILURE); } exit(0); } void *thread_func(void* arg) { int exit_code,i; //进入之后,先设置自己为不可取消状态 printf("I am thread,now my cancle type is disable\n"); if(pthread_setcancelstate(PTHREAD_CANCEL_DISABLE,NULL)!=0){ fprintf(stdout,"pthread_setcancelstate error\n"); exit_code=-1; pthread_exit(&exit_code); } for(i=1;i<=3;i++){ sleep(1); printf("thread running (%d)\n",i); } //休眠3秒之后之后设置线程可以被取消 printf("I am thread,now my cancle type is enable\n"); if(pthread_setcancelstate(PTHREAD_CANCEL_ENABLE,NULL)!=0){ fprintf(stdout,"pthread_setcancelstate error\n"); exit_code=-1; pthread_exit(&exit_code); } printf("thread sleep....\n"); sleep(20); pthread_exit(NULL); }
[Separação de tópicos: pthread_detach]
- Se o encadeamento não for separado: o status de encerramento do encadeamento será mantido até que outro encadeamento chame pthread_join no encadeamento para obter seu status de encerramento, o status de encerramento não será liberado
- Se o encadeamento foi desanexado: os recursos de armazenamento subjacentes do encadeamento podem ser recuperados imediatamente quando o encadeamento termina
- Depois que o encadeamento é desanexado, não podemos usar a função pthread_join para aguardar seu estado de término, porque chamar pthread_join no encadeamento no estado desanexado produzirá um comportamento indefinido
#include <pthread.h> int pthread_detach(pthread_t tid);
- Valor de retorno: retorna 0 se for bem sucedido; caso contrário, retorna o número do erro
- Função: usado para separar threads
- Parâmetros: o tid do fio a ser destacado
[Exclusão mútua: pthread_mutex_t]
- O mutex é essencialmente um bloqueio. O mutex é definido (bloqueado) antes de acessar recursos compartilhados e o mutex é liberado (desbloqueado) após o acesso ser concluído
- Inicialização e liberação de variáveis mutuamente exclusivas
- ① Inicialização estática
- Defina a variável mutex pthread_mutex_t diretamente para a constante PTHREAD_MUTEX_INITIALIZER
- Variáveis mutex inicializadas estaticamente podem ter apenas os atributos mutex padrão e não podem definir outros atributos mutex
pthread_mutex_t mutex; mutex=PTHREAD_MUTEX_INITIALIZER; //或者 pthread_mutex_t *mutex=(pthread_mutex_t *)malloc(sizeof(pthread_mutex_t)); *mutex=PTHREAD_MUTEX_INITIALIZER;
- ② Inicialização dinâmica
- Variáveis mutex inicializadas estaticamente podem ter apenas os atributos mutex padrão, podemos inicializar dinamicamente o mutex através da função pthread_mutex_init, e você pode escolher definir os atributos mutex durante a inicialização
#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr); int pthread_mutex_destroy(pthread_mutex_t *mutex);
- Valor de retorno: retorna 0 se bem-sucedido, caso contrário, retorna um número de erro
- p th read_mutex_init :
- Função: inicializar o mutex
- parâmetro:
- Parâmetro 1: o mutex precisa ser inicializado
- Parâmetro 2: as propriedades do mutex durante a inicialização. Se você usar os atributos padrão, preencha NULL aqui
- pthread_mutex_destroy :
- Função: Desinicialização do mutex
- Parâmetros: Mutex
- Comentários (ênfase): Esta função apenas desinicializa o mutex e não libera o espaço de memória. Se o mutex for solicitado por meio de uma função como malloc, você precisará chamar a função pthread_mutex_destroy antes de liberar o mutex
pthread_mutex_t mutex; pthread_mutex_init(&mutex,NULL); /*do something*/ pthread_mutex_destroy(&mutex); pthread_mutex_t* mutex=(pthread_mutex_t*)malloc(sizeof(pthread_mutex_t)); pthread_mutex_init(mutex, NULL); /*do something*/ pthread_mutex_destroy(mutex); free(mutex);
[Bloqueio e desbloqueio de mutex]
#include <pthread.h> int pthread_mutex_lock(pthread_mutex_t *mutex); //对互斥量进行加锁。如果互斥量已经上锁,调用线程将阻塞到互斥量被解锁 int pthread_mutex_trylock(pthread_mutex_t *mutex); //对互斥量进行解锁 int pthread_mutex_unlock(pthread_mutex_t *mutex);
- Tente bloquear o mutex (sem bloqueio). Se o mutex estiver em um estado desbloqueado, então pthead_mutex_trylock irá bloquear o mutex; se o bloqueio estiver em um estado bloqueado, então pthead_mutex_trylock retornará EBUSY em erro e não bloqueará
[Caso de contagem de referência]
struct foo { int f_count; pthread_mutex_t f_lock; int f_id; }; struct foo *foo_alloc(int id) { struct foo *fp; if ((fp == malloc(sizeof(struct foo))) != NULL) { fp->f_count = 1; fp->f_id = id; if (pthread_mutex_init(&fp->f_lock, NULL) != 0) { free(fp); fp = NULL; } } return fp; } void foo_hold(struct foo *fp) { pthread_mutex_lock(&fp->f_lock); fp->f_count++; pthread_mutex_unlock(&fp->f_lock); } void foo_rele(struct foo *fp) { pthread_mutex_lock(&fp->f_lock); if (--fp->f_count == 0) { pthread_mutex_unlock(&fp->f_lock); pthread_mutex_destory(&fp->f_lock); free(fp); } else { pthread_mutex_unlock(&fp->f_lock); } }
[Timeout mutex: pthread_mutex_timedlock]
#include <pthread.h> #include <time.h> int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex,const struct timespec *restrict tsptr);
- Valor de retorno: retorna 0 em caso de sucesso, caso contrário, retorna um número de erro.
- Função: Quando um thread tenta adquirir um mutex bloqueado, pthread_mutex_timedlock permite o tempo de bloqueio do thread de ligação
- parâmetro:
- mutex: o mutex para tentar bloquear
- tsptr: definir tempo limite
- Características: Quando o valor de tempo limite for atingido, se o mutex não puder ser bloqueado com sucesso, o código de erro ETIMEDOUT será retornado
- Nota sobre hora extra: hora extra especifica o tempo absoluto que está disposto a esperar. Este valor de tempo é um número absoluto em vez de um número relativo.
- Por exemplo, se você está disposto a esperar 3 minutos, em vez de converter 3 minutos em uma estrutura de especificação de tempo e passá-lo para o parâmetro 2, você precisa adicionar 3 minutos à hora atual e, em seguida, convertê-la em uma estrutura de especificação de tempo e depois passá-la para o parâmetro 2.
[Pthread_mutex_timedlock evita casos de bloqueio permanente]
#include <pthread.h> int main(void) { int err; struct timespec tout; struct tm *tmp; char buf[64]; pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_lock(&lock); printf("mutex is locked\n"); clock_gettime(CLOCK_REALTIME, &tout); tmp = localtime(&tout.tv_sec); strftime(buf, sizeof(buf), "%r", tmp); printf("current time is %s\n", buf); tout.tv_sec += 10; /* 10 seconds from now */ /* caution: this could lead to deadlock */ err = pthread_mutex_timedlock(&lock, &tout); clock_gettime(CLOCK_REALTIME, &tout); tmp = localtime(&tout.tv_sec); strftime(buf, sizeof(buf), "%r", tmp); printf("the time is now %s\n", buf); if (err == 0) printf("mutex locked again!\n"); else printf("can’t lock mutex again: %s\n", strerror(err)); exit(0); }
- Este programa bloqueia deliberadamente seu mutex existente para demonstrar como funciona o pthread_mutex_timedlock. Não é recomendado usar este método na prática, porque levará a um impasse
- Observação: o tempo de bloqueio pode ser diferente. Há muitos motivos para diferentes motivos: o tempo de início pode ser no meio de um determinado segundo, a precisão do relógio do sistema pode não ser precisa o suficiente para suportar o valor de tempo limite especificado por nós, ou o programa continua. Antes da execução, o atraso na programação pode aumentar o valor do tempo
[Variável de condição: pthread_cond_t]
- Variáveis de condição são outro mecanismo de sincronização disponível para threads
- Variáveis de condição fornecem um ponto de encontro para vários tópicos
- Quando variáveis de condição e mutex são usados juntos, os encadeamentos podem esperar que condições específicas ocorram de maneira não competitiva
- Variáveis de condição são coisas na thread, ou seja, esperando que uma determinada condição ocorra, assim como um sinal
- A variável de condição deve ser usada com o mutex, e a própria condição é protegida pelo mutex. O thread deve primeiro bloquear o mutex antes de alterar o estado da condição
- Outros threads não saberão dessa mudança antes de obter o mutex, porque o mutex deve ser bloqueado antes que a condição possa ser calculada
- ① Inicialização estática
- Defina diretamente a variável de condição definida por pthread_cond_t para a constante PTHREAD_COND_INITIALIZER
- Variáveis de condição inicializadas estaticamente podem ter apenas atributos de variáveis de condição padrão e não podem definir outros atributos de variáveis de condição
pthread_cond_t cond; cond=PTHREAD_COND_INITIALIZER; //或者 pthread_cond_t *cond=(pthread_cond_t *)malloc(sizeof(pthread_cond_t)); *cond=PTHREAD_COND_INITIALIZER;
- ② Inicialização dinâmica
- Variáveis de condição inicializadas estaticamente só podem ter atributos de variáveis de condição padrão, podemos inicializar dinamicamente as variáveis de condição por meio da função pthread_mutex_init e podemos escolher definir os atributos da variável de condição durante a inicialização
#include <pthread.h> int pthread_cond_init(pthread_cond_t* restrict cond,const pthread_condattr_t* restrict attr); int pthread_cond_destroy(pthread_cond_t* cond);
- Valor de retorno: retornar 0 em caso de sucesso; retornar o número do erro em caso de falha
- pthread_cond_init :
- Função: inicializar a variável de condição
- parâmetro:
- Parâmetro 1: a variável de condição que precisa ser inicializada
- Parâmetro 2: O atributo da variável de condição durante a inicialização. Se você usar os atributos padrão, preencha NULL aqui
- pthread_cond_destroy :
- Função: Desinicialize a variável de condição (antes que a variável de condição libere a memória)
- Parâmetros: variáveis de condição
- Comentários (ênfase): Esta função apenas desinicializa o mutex e não libera o espaço de memória. Se o mutex for solicitado por meio de uma função como malloc, você precisará chamar a função pthread_mutex_destroy antes de liberar o mutex
pthread_cond_t cond; pthread_cond_init(&cond,NULL); /*do something*/ pthread_cond_destroy(&cond); pthread_cond_t * cond=(pthread_cond_t *)malloc(sizeof(pthread_cond_t)); pthread_cond_init(cond,NULL); /*do something*/ pthread_cond_destroy(cond); free(cond);
[Aguarde a função de variável de condição]
#include <pthread.h> int pthread_cond_wait(pthread_cond_t* restrict cond,pthread_mutex_t* restrict mutex); int pthread_cond_timedwait(pthread_cond_t* cond,pthread_mutex_t* restrict mutex,const struct timespec* restrict tsptr);
- Valor de retorno: retornar 0 em caso de sucesso; retornar o número do erro em caso de falha
- Quando essas duas chamadas de função retornam com sucesso, o thread precisa recalcular a condição, porque outro thread pode já estar em execução e alterar a condição
- pthread_cond_wait
- Nota: Espere até que a variável de condição se torne verdadeira
- Como usar: O parâmetro mutex mutex é bloqueado antecipadamente, e então o mutex protege a condição, esperando que a variável de condição do parâmetro 1cond se torne verdadeira. No processo de espera que a variável de condição se torne verdadeira, esta função foi bloqueada. Mas quando em um estado de bloqueio, o mutex mutex é desbloqueado (porque outros threads precisam usar esse bloqueio para tornar a variável de condição verdadeira)
- Quando a função pthread_cond_wait retorna, o mutex é bloqueado novamente
- pthread_cond_timedwait
- A função pthread_cond_timedwait tem a mesma função que a função pthread_cond_wait. Mas há mais um parâmetro de tempo limite. O valor do tempo limite especifica quanto tempo estamos dispostos a esperar, que é representado pela estrutura de timespec
- Se a condição não aparecer após o tempo limite expirar, esta função irá recuperar o mutex e retornar o erro ETIMEOUT
- Nota: este valor de tempo é um número absoluto e não um número relativo. Por exemplo, se você deseja esperar 3 minutos, em vez de converter 3 minutos em uma estrutura de especificação de tempo, você precisa adicionar 3 minutos ao horário atual e então convertê-lo em uma estrutura de especificação de tempo
[Obtenha a função de aquisição de tempo absoluto do valor de tempo limite]
- Você pode usar a função clock_gettime para obter a hora atual representada pela estrutura timespec. Mas nem todas as plataformas atualmente oferecem suporte a essa função. Portanto, você pode usar gettimeofday para obter a hora atual representada pela estrutura timeval e, em seguida, converter essa hora em uma estrutura de especificação de tempo
#include <sys/time.h> #include <stdlib.h> void maketimeout(struct timespec *tsp, long minutes) { struct timeval now; /* get the current time */ gettimeofday(&now, NULL); tsp->tv_sec = now.tv_sec; tsp->tv_nsec = now.tv_usec * 1000; /* usec to nsec */ /* add the offset to get timeout value */ tsp->tv_sec += minutes * 60; }
【Função de envio de sinal variável de condição】
#include <pthread.h> int pthread_cond_signal(pthread_cond_t* cond); //至少能唤醒一个等待该条件的线程 int pthread_cond_broadcast(pthread_cond_t* cond); //唤醒等待该条件的所有线程
- Valor de retorno: retornar 0 em caso de sucesso; retornar o número do erro em caso de falha
- Essas duas funções são usadas para notificar a thread de que a variável de condição atendeu à condição (torna-se verdadeira). Quando essas duas funções são chamadas, elas estão sinalizando o thread ou condição
- Atenção deve ser dada: o thread deve ser sinalizado após alterar o estado de condição
[Combine o uso de variáveis de condição e mutex para sincronizar threads]
#include <pthread.h> struct msg { struct msg *m_next; /* ... more stuff here ... */ }; struct msg *workq; pthread_cond_t qready = PTHREAD_COND_INITIALIZER; pthread_mutex_t qlock = PTHREAD_MUTEX_INITIALIZER; void process_msg(void) { struct msg *mp; for (;;) { pthread_mutex_lock(&qlock); while (workq == NULL) pthread_cond_wait(&qready, &qlock); mp = workq; workq = mp->m_next; pthread_mutex_unlock(&qlock); /* now process the message mp */ } } void enqueue_msg(struct msg *mp) { pthread_mutex_lock(&qlock); mp->m_next = workq; workq = mp; pthread_mutex_unlock(&qlock); pthread_cond_signal(&qready); }