1 Visão geral do segmento
1.1 Introdução aos threads
No ambiente Linux, o chamado thread é um processo leve. O sistema operacional aloca recursos em unidades de processos e usa vários pequenos processos para concluir diferentes tarefas simultaneamente em um espaço de execução.Esses pequenos processos são chamados de threads. No encadeamento do mesmo processo, existem recursos compartilhados e recursos privados, da seguinte maneira:
- Recursos compartilhados: espaço de endereço, variáveis globais, arquivos abertos, processos filhos, alarmes, sinais e programas de serviço de sinais, informações contábeis
- Recursos privados: contadores de programas, registros, pilhas, palavras de status.
1.2 Modelo de implementação de encadeamento do sistema operacional moderno
O modelo de encadeamento de sistemas operacionais modernos é baseado no modelo de encadeamento anterior no modo de usuário e no encadeamento no modo kernel, ou em uma combinação dos dois. O sistema de execução no modo de usuário é responsável pela alternância de encadeamentos internos no processo quando não está bloqueado; o sistema operacional no modo kernel é responsável pela alternância de encadeamentos bloqueados. Há menos threads no modo kernel e mais threads no modo user. Cada thread no modo kernel atende a um ou mais threads no modo user. Ao alocar encadeamentos, geralmente defina os encadeamentos que precisam executar operações de bloqueio como encadeamentos no modo kernel e defina os encadeamentos que não executarão operações de bloqueio como encadeamentos no modo de usuário.
1.3 Vantagens do multithreading
Aumente a simultaneidade do programa, melhorando assim a eficiência operacional do programa.
1.4 A programação de threads precisa prestar atenção ao
- Este conjunto de funções pthread é inútil no manual do homem, porque é uma biblioteca pthread separada.
- As funções relacionadas ao pthread estão no arquivo de cabeçalho pthread.h.
- Geralmente, ocorrem erros durante a compilação direta.Você deve adicionar a biblioteca -lpthread.
2 Identificador de segmento
Cada encadeamento possui seu próprio ID, representado pelo tipo de dados pthread_t, que é essencialmente um número inteiro não assinado, ou seja, typedef unsigned int pthread_t; por motivos de portabilidade, o tipo pthread_t não pode ser equivalente a um número inteiro não assinado Digite operação.
2.1 Função pthread_self
Use a função pthread_self para obter o ID de um thread no Linux
pthread_t pthread_self(void);
函数返回本线程的线程ID。
2.2 Função pthread_equal
Use a função pthread_equal para comparar se dois IDs de encadeamento são iguais no Linux
int pthread_equal(pthread_t tid1,pthread_t tid2);
参数tid1\tid2:表示将要比较的量个线程ID号。
如果两个值相同,返回0,不相等则返回非零值。
3 Função de criação de thread pthread_creat
Use a função pthread_creat para criar um thread no Linux:
int pthread_create (pthread_t * restrict tidp , pthread_attr_t *restrict \
attr,void *(*start_routine) (void *),void *restrict arg);
参数tidp:pthread_t类型的指针,一个值结果参数。
参数attr:线程的属性,不想指定特定属性的时候置为NULL。
参数start_rtn:一个函数指针(返回值是一个指向void型的指针,参数也是一个指向void型的指针),创建的线程要从该函数的起始位置处开始执行,函数返回该线程就停止了。
参数arg:一个void型的指针,是函数start_rtn的参数,在线程开始执行时,该参数由内核传递给线程。
函数执行成功返回0,失败返回错误编号。这种规律适合于所有的线程系列函数。
Nota:
- Threads e processos no Linux têm direitos iguais de agendamento.A ordem de agendamento de um thread e de outro thread é completamente imprevisível, o que depende do algoritmo de agendamento do kernel.
- Se você deseja passar vários parâmetros para a função pthread_creat, é necessário organizar todos os parâmetros em uma estrutura e depois passar o endereço da estrutura como parâmetro para o novo encadeamento.
- As funções de série de encadeamentos geralmente não definem o valor de errno, mas retornam o número do erro. Como os threads podem acessar livremente as variáveis de ambiente do processo, quando vários threads cometerem erros, o valor de errno será substituído várias vezes e o processo poderá verificar apenas a causa do último thread de erro.
4 Terminação de thread
4.1 Existem 3 maneiras de finalizar o thread
- A execução da função de encadeamento termina: use o encadeamento criado por pthread_creat para executar uma função e saia do encadeamento se a execução da função terminar. Semelhante ao retorno da função principal.
- Cancelado por outro thread: semelhante a ser morto entre processos.
- O encadeamento sai por si próprio: semelhante a chamar a função de saída.
4.2 Função pthread_exit
Use a função pthread_exit para finalizar o encadeamento no Linux:
void pthread_exit(void *rval_ptr);
参数rval_ptr:一个指向void型的指针,该指针指向的区域存储退出信息,该信息类似于传递给新线程的参数,将多个信息组织成一个结构体。
函数无返回值。
Existem dois tipos de informações finais para um encadeamento:
- A área apontada pelo ponteiro retornada pela função do corpo da rosca. Obtenha o valor de retorno da função do corpo do encadeamento.
- A área apontada pela função pthread_exit. Obtenha as informações de saída definidas pela função pthread_exit.
4.3 Função pthread_join
Use pthread_join para acessar as informações finais de um encadeamento especificado no Linux:
int pthread_join(pthread_t tid,void **rval_ptr);
参数tid:需要取得结束信息的线程。(如果该线程正在运行,则等待,直到该线程结束执行为止;如果指定线程的ID和调用线程的ID不属于同一进程,则出错)。
参数rval_ptr:一个指向void型的指针(因为要在内核中改变它的值)
//如果线程由于线程体函数返回/调用pthread_exit函数退出,则参数指向的是退出信息的首地址。
//如果其他线程由于被其他线程取消而退出则该参数被置为PTHREAD_CANCELED常量。
//如果不关心返回值,则将参数置为NULL。这时仅仅等待线程执行结束,不获得线程退出信息。
函数执行成功返回0,失败返回错误号。
O modelo geral de uso de encadeamento é:
pthread_create(); //创建第1个线程
pthread_create(); //创建第2个线程
...
pthread_create(); //创建第n个线程
...
...
pthread_join(); //得到第1个线程的退出信息
pthread_join(); //得到第2个线程的退出信息
...
pthread_join(); //得到第n个线程的退出信息
4.4 Como sair da mensagem corretamente
Depois que o thread termina a execução, o kernel do Linux salva apenas o primeiro endereço da área de memória onde as informações de saída são armazenadas e não armazena as informações de saída no kernel.Em geral, memória alocada dinamicamente / variáveis globais são usadas para salvar as informações de saída do thread.
4.5 Cancelar a execução de um encadeamento pthread_cancel
Um segmento pode ser cancelado por outro segmento. Use a função pthread_cancel para cancelar outro encadeamento no Linux:
int pthread_cancel(pthread_t tid);
参数tid:要取消线程的线程ID。
函数执行成功返回0,失败返回错误编号。
Chamar a função pthread_cancel é equivalente a chamar o próprio thread cancelado: pthread_exit (PTHREAD_CANCELED);
4.6 Função de limpeza de threads pthread_cleanup_push / pthread_cleanup_pop
Use pthread_cleanup_push / pthread_cleanup_pop para obter o fim da limpeza de encadeamentos no Linux:
void pthread_cleanup_push(void (*rtn)(void *),void *arg); //设置清理程序
void pthread_cleanup_pop(int execute); //执行清理程序
参数rtn: 处理程序入口地址。
参数arg: 传递给处理函数的参数。
参数execute:
0:不执行清理程序。但是将栈顶的清理程序记录出栈。
非0:执行栈顶清理程序。执行后栈顶的清理程序记录出栈。
函数无返回值。
Nota: A função pthread_cleanup_push será executada em 3 situações:
- Chame a função pthread_exit.
- Cancelado por outros tópicos.
- Ao chamar a função pthread_cleanup_pop com um parâmetro de valor diferente de zero.
pthread_push e pthread_pop devem ser usados em pares, caso contrário, ocorrerá um erro. Como pthread_cleanup_push () e pthread_cleanup_pop () são implementados no modo macro, esta é a definição de macro em pthread.h:
#define pthread_cleanup_push(routine,arg) \
{
struct _pthread_cleanup_buffer _buffer; \
_pthread_cleanup_push (&_buffer, (routine), (arg));
#define pthread_cleanup_pop(execute) \
_pthread_cleanup_pop (&_buffer, (execute)); \
}
Pode-se ver que pthread_cleanup_push () carrega um "{", enquanto pthread_cleanup_pop () carrega um "}". A ordem de definir o procedimento de limpeza e executar o procedimento de limpeza é inversa.
4.7 Função pthread_detach de separação de threads
Use a função pthread_detach no Linux para obter uma separação dinâmica de threads:
int pthread_detach(pthread tid);
参数tid:要使之处于分离状态的线程ID。
Essa função é definir o status do encadeamento filho como desanexado; todos os recursos serão liberados automaticamente após a execução do encadeamento.
5 Thread synchronization_mutexes pthread_mutex _ ***
Um mutex é um tipo de bloqueio, que é adicionado ao acessar um recurso compartilhado e liberado no final, de modo que apenas um encadeamento esteja na zona crítica a qualquer momento. Quem quiser entrar na seção crítica testará a trava.Se a trava já estiver presa por uma linha, a linha de teste será bloqueada até que a trava seja liberada e a linha repita o processo acima.
5.1 Inicializando e destruindo o mutex
No ambiente Linux, o tipo de dados pthread_mutex_t é usado para representar o mutex e precisa ser inicializado quando usado. Quando não estiver em uso, destrua-os.
pthread_mutex_init:锁的初始化
int pthread_mutex_init(pthread_mutex_t *mutex, const
pthread_mutexattr_t *mutexattr)
pthread_mutex_destroy:锁的销毁
int pthread_mutex_destroy(pthread_mutex_t *mutex);
参数mutex:互斥锁的指针,一种特殊的类型参数。
参数mutexattr:锁的属性,缺省值为NULL。
函数执行成功返回0,失败返回错误号。
Existem duas maneiras de criar um mutex: estático e dinâmico. O POSIX define uma macro PTHREAD_MUTEX_INITIALIZER para inicializar estaticamente o mutex, da seguinte maneira:
pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER;
/*(在 LinuxThreads 实 现 中 , pthread_mutex_t 是 一 个 结 构 ,\
而PTHREAD_MUTEX_INITIALIZER 则是一个结构常量)*/
A maneira dinâmica é usar a função pthread_mutex_init () para inicializar o mutex. A API é definida da seguinte maneira:
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr)
Mutexattr é usado para especificar o atributo do mutex.Se for NULL, o atributo padrão será usado.
5.2 Obtenção e liberação do mutex
Bloqueie-o quando estiver em uso. Desbloqueie a trava no final de sua operação. As funções envolvidas são:
//pthread_mutex_lock:加锁操作
int pthread_mutex_lock(pthread_mutex_t *mutex);
//pthread_mutex_trylock:加锁操作
int pthread_mutex_trylock(pthread_mutex_t *mutex);
//pthread_mutex_unlock:解锁操作
int pthread_mutex_unlock(pthread_mutex_t *mutex);
参数mutex:互斥锁的指针,一种特殊的类型参数。
函数执行成功返回0,失败返回错误号。
A diferença entre as funções pthread_mutex_lock e pthread_mutex_trylock:
- Se o bloqueio solicitado pelo primeiro for adquirido por um encadeamento, ele será bloqueado até que o mutex seja liberado.
- Se o bloqueio solicitado pelo último for adquirido por um encadeamento, ele não será bloqueado e retornará imediatamente um número de erro EBUSY, indicando que o bloqueio aplicado está ocupado.
6 Bloqueio de sincronização de leitura e gravação de encadeamento pthread_rwlock _ ***
Um bloqueio mutex possui apenas um thread por vez para operar o bloqueio, e outros threads são bloqueados porque não podem obtê-lo. A intenção original de criar uma operação multithread é executar tarefas simultaneamente, mas devido ao bloqueio de mutex, a operação do thread se torna serial e a eficiência do programa será reduzida. Durante a execução do programa, se houver muito mais threads lendo um recurso compartilhado do que escrevendo threads, o uso desses bloqueios de leitura e gravação poderá aumentar bastante a simultaneidade dos threads, melhorando assim a eficiência operacional dos threads.
6.1 Inicializando e destruindo bloqueios de leitura e gravação
O ambiente linux usa o tipo de dados pthread_rwlock_t para indicar o tipo de bloqueio de leitura e gravação, que precisa ser inicializado quando usado. Quando não estiver em uso, destrua-os.
//pthread_rwlock_init:初始化读写锁
int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
//pthread_rwlock_destroy:销毁读写锁
int pthread_rwlock_destroy(pthread_rwlock_t *restrict rwlock);
参数rwlock:读写锁的指针。
参数attr:读写锁的属性,缺省值为NULL。
函数执行成功返回0,失败返回错误号。
6.2 Obtenção e liberação do bloqueio de leitura e gravação
//pthread_rwlock_rdlock:设定读锁
int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
//pthread_rwlock_wrlock:设定写锁
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。
A implementação pode limitar o número de bloqueios no modo de leitura do bloqueio de leitura e gravação, portanto, precisamos verificar o valor de retorno para determinar se é bem-sucedido. As outras duas funções retornarão um erro, mas enquanto nosso design de bloqueio for apropriado, não podemos verificar. As funções sem bloqueio são:
//pthread_rwlock_rdlock:设定读锁
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
//pthread_rwlock_wrlock:设定写锁
int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。
//当锁成功获取时,返回0,否则返回EBUSY。
//这两个函数可以避免死锁。如果针对未初始化的读写锁调用进行读写操作,则结果是不确定的。
pthread_rwlock_unlock: libera o bloqueio de leitura e gravação (usado para liberar o bloqueio mantido no objeto de bloqueio de leitura e gravação referenciado por rwlock.)
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
参数rwlock:读写锁的指针。
函数执行成功返回0,失败返回错误号。
7 Variável synchronization_condition de encadeamento pthread_cond _ ***
7.1 Inicialização e destruição de variáveis de condição
O tipo de dados da variável de condição é pthread_cond_t e deve ser inicializado antes do uso.
//pthread_cond_init:条件变量初始化
int pthread_cond_init(pthread_cond_t *restrict cond,pthread_condattr_t *restrict attr);
//pthread_cond_destroy:条件变量销毁
int pthread_cond_destroy(pthread_cond_t *cond);
参数cond:条件变量,一个特殊的参数类型。
参数attr:条件变量的属性,缺省值为NULL。
函数执行成功则返回0, 出错则返回错误编号。
Duas maneiras de inicializar variáveis de condição:
- Estático: A constante PTHREAD_COND_INITIALIZER pode ser atribuída a uma variável de condição atribuída estaticamente.
- Dinâmico: A função pthread_cond_init é limpar o espaço de memória da variável de condição dinâmica com pthread_cond_destroy antes de liberá-lo.
7.2 Condições de espera para variáveis de condição
A função de condição de espera espera que a condição se torne verdadeira.O mutex passado para pthread_cond_wait protege a condição.O chamador passa o mutex bloqueado para a função. A função coloca o thread de chamada na lista de espera de threads e desbloqueia o mutex.Estas duas operações são atômicas. Isso fecha o canal de tempo entre a verificação da condição e o encadeamento entra em suspensão e aguarda a alteração da condição, para que o encadeamento não perca nenhuma alteração na condição. Quando pthread_cond_wait retorna, o mutex é bloqueado novamente.
//pthread_cond_wait:阻塞等待
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restric mutex);
//pthread_cond_timewait:超时等待
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict timeout);
参数cond:条件变量,一个特殊的参数类型。
参数mutex:等待的指定的锁,一般在其他线程内。
参数timeout:等待的时间。
函数执行成功则返回0, 出错则返回错误编号。
A diferença entre as duas funções: as duas funcionam de maneira semelhante, exceto que a função pthread_cond_timedwait tem um tempo limite extra que especifica o período de tempo limite.
7.3 Condições de notificação para variáveis de condição
As duas funções a seguir são usadas para notificar o encadeamento de que a condição foi atendida: chamar essas duas funções também é chamado de enviar um sinal para o encadeamento ou condição.Deve-se observar que o sinal deve ser enviado ao encadeamento após alterar o estado da condição.
pthread_cond_signal:
int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_broadcast:
int pthread_cond_broadcast(pthread_cond_t *cond);
参数cond:条件变量,一个特殊的参数类型。
成功则返回0, 出错则返回错误编号。
A diferença entre as duas funções:
- pthread_cond_signal é ativar um encadeamento aguardando esta condição.
- pthread_cond_broadcast é ativar todos os threads que aguardam essa condição.