Programação do sistema Linux C (10) operação básica de gerenciamento de threads

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:

  1. 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
  2. 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

  1. Este conjunto de funções pthread é inútil no manual do homem, porque é uma biblioteca pthread separada.
  2. As funções relacionadas ao pthread estão no arquivo de cabeçalho pthread.h.
  3. 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:

  1. 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.
  2. 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.
  3. 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

  1. 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.
  2. Cancelado por outro thread: semelhante a ser morto entre processos.
  3. 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:

  1. 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.
  2. 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:

  1. Chame a função pthread_exit.
  2. Cancelado por outros tópicos.
  3. 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:

  1. Se o bloqueio solicitado pelo primeiro for adquirido por um encadeamento, ele será bloqueado até que o mutex seja liberado.
  2. 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:

  1. Estático: A constante PTHREAD_COND_INITIALIZER pode ser atribuída a uma variável de condição atribuída estaticamente.
  2. 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:

  1. pthread_cond_signal é ativar um encadeamento aguardando esta condição.
  2. pthread_cond_broadcast é ativar todos os threads que aguardam essa condição.
     
Publicado 289 artigos originais · elogiou 47 · 30.000+ visualizações

Acho que você gosta

Origin blog.csdn.net/vviccc/article/details/105165155
Recomendado
Clasificación