Tópico de junção C ++

Tópico de junção C ++
Os tópicos são como subprogramas para um programa. E assim, eles correriam de forma independente, a menos que se unissem. A união significa que um thread será executado até atingir um determinado ponto e depois parar e aguardar que outro thread termine sua execução (até o fim); Antes de continuar sua própria execução. No ponto em que o primeiro tópico termina, há uma declaração de junção. Um tópico pode chamar outro tópico. É o tópico que chama, que se junta. O fio chamado não se une.

A união existe porque os tópicos precisam se comunicar. Após a mudança, o encadeamento chamado pode alterar o valor de uma variável global, que o encadeamento de chamada precisa acessar. Esta é uma forma de sincronização.

Este artigo explica duas maneiras de ingressar em tópicos. Começa com uma ilustração do que é um tópico.

Conteúdo do artigo

  • Fio
  • Juntando -se a um tópico
  • futuro :: get ()
  • Conclusão

Fio

Considere o seguinte programa:

#incluir
usando namespace std;
void fn2 ()
cout << "function 2" << '\n';

void fn1 ()
cout << "function 1" << '\n';

int main ()

/ * Algumas declarações */
retornar 0;

fn1 (), fn2 () e main () são funções de nível superior, embora main () seja uma função-chave. Três tópicos podem ser obtidos nessas três funções de nível superior. O programa curto muito simples a seguir é um tópico natural:

#incluir
usando namespace std;
int main ()

/ * Algumas declarações */
retornar 0;

A função principal () se comporta como um tópico. Pode ser considerado como o tópico principal. Não precisa ser encapsulado em nenhuma instanciação da classe Thread. Portanto, o programa anterior com funções de nível superior que inclui a função principal () ainda é um tópico. O programa a seguir mostra como as duas funções, FN1 () e FN2 () podem ser convertidas em threads, mas sem nenhuma declaração de junção:

#incluir
#incluir
#incluir
usando namespace std;
string globl1 = string ("SO");
string globl2 = string ("Seja isso!");
void fn2 (string st2)
string globl = GLOBL1 + ST2;
cout << globl << endl;

void fn1 (string st1)
GLOBL1 = "Sim. " + st1;
Thread Thr2 (FN2, GLOBL2);

int main ()

Thread Thr1 (FN1, GLOBL1);
/ * Algumas declarações */
retornar 0;

O programa começa com a inclusão da biblioteca iostream para o objeto Cout. Então a biblioteca de threads está incluída. Incluir a biblioteca de threads é uma obrigação; para que o programador simplesmente instancie um objeto de thread da classe Thread usando uma função de nível superior, exceto a função principal ().

Depois disso, a biblioteca de cordas está incluída. Esta biblioteca simplifica o uso de literais de cordas. A primeira declaração no programa insiste que qualquer nome usado é do espaço de nome padrão C ++, a menos que indicado de outra forma.

As próximas duas declarações declaram dois objetos globais de string com seus literais. Os objetos da string são chamados GLOBL1 e GLOBL2. Existe a função fn1 () e a função fn2 (). O nome da função fn2 () será usado como um dos argumentos para instanciar o thread, Thr2, da classe Thread. Quando uma função é instanciada dessa maneira, a função é chamada; e ele executa. Quando a função fn2 () é chamada, concatena os literais de cordas de GLOBL1 e GLOBL2 para ter “então, seja, seja!”. GLOBL2 é o argumento de fn2 ().

O nome da função fn1 () é usado como um argumento para a instanciação do thread, Thr1, da classe Thread. Durante esta instanciação, Fn1 () é chamado. Quando é chamado, precede a string: “Então, seja!”Com" sim. ", Ter" sim. Que assim seja!", que é a saída para todo o programa de threads.

A função principal (), que é o encadeamento principal (), instancia o thread, Thr1 da classe Thread, com os argumentos FN1 e GLOBL1. Durante esta instanciação, Fn1 () é chamado. A função, fn1 () é o encadeamento efetivo para o objeto, thr1. Quando uma função é chamada, ela deve ser executada do início até o fim.

Thr1, que é efetivamente FN1 (), instancia o fio, Thr2, da classe Thread, com os argumentos FN2 e GLOBL2. Durante esta instanciação, Fn2 () é chamado. A função, fn2 () é o encadeamento efetivo para o objeto, thr2. Quando uma função é chamada, ela deve ser executada do início até o fim.

Se o leitor estiver usando, o compilador G ++, ele poderá testar este programa de threads, para compilação C ++ 20, com o seguinte comando:

g ++ -std = c ++ 2a temp.cpp -lpthread -o temp

O autor fez isso; executou o programa e teve a saída:

rescindir chamado sem uma exceção ativa
Abortado (núcleo despejado)

É possível ter uma saída errônea como essa, com a saída adequada de "sim. Que assim seja!", Slotado dentro. No entanto, tudo o que ainda é inaceitável.

O problema com esta saída errônea é que os threads não foram unidos. E assim, os tópicos operavam de forma independente, levando à confusão. A solução é ingressar no Thr1 no tópico principal e, como o Thr1 chama Thr2, assim como o thread principal thr1, Thr2 deve ser unido para Thr1. Isso é ilustrado na próxima seção.

Juntando -se a um tópico

A sintaxe para ingressar em um tópico para o tópico de chamada é:

Threadobj.juntar();

onde junção () é uma função de membro de um objeto Thread. Esta expressão tem que estar dentro do corpo do tópico de chamada. Esta expressão deve estar dentro do corpo da função de chamada, que é um fio eficaz.

O programa a seguir é o programa acima repetido, mas com o corpo do tópico principal que entra o Thr1, e o corpo de Thr1 juntando -se a Thr2:

#incluir
#incluir
#incluir
usando namespace std;
string globl1 = string ("SO");
string globl2 = string ("Seja isso!");
void fn2 (string st2)
string globl = GLOBL1 + ST2;
cout << globl << endl;

void fn1 (string st1)
GLOBL1 = "Sim. " + st1;
Thread Thr2 (FN2, GLOBL2);
Thr2.juntar();

int main ()

Thread Thr1 (FN1, GLOBL1);
Thr1.juntar();
/ * Algumas declarações */
retornar 0;

Observe as posições de espera, onde as declarações de junção foram inseridas no programa. A saída é:

"Sim. Que assim seja!”

Sem as citações, como esperado, limpo e claro, inequívoco ”. Thr2 não precisa de nenhuma declaração de junção em seu corpo; não chama nenhum tópico.

Então, o corpo do tópico de chamada se junta ao fio chamado.

futuro :: get ()

A biblioteca padrão C ++ tem uma sub-biblioteca chamada Future. Esta sub-biblioteca tem uma classe chamada Future. A biblioteca também tem uma função chamada Async (). A classe, Future, tem uma função de membro chamada get (). Além de seu papel principal, essa função tem o efeito de unir duas funções que estão funcionando simultaneamente ou em paralelo. As funções não precisam ser tópicos.

A função async ()

Observe que os fios acima de tudo retornam vazios. Um tópico é uma função que está sob controle. Algumas funções não retornam vazios, mas retornam algo. Então, alguns tópicos retornam algo.

A função Async () pode assumir uma função de nível superior como um argumento e executar a função simultaneamente ou em paralelo com a função de chamada. Nesse caso, não há threads, apenas uma função de chamada e uma função chamada chamada de argumento para a função async (). Uma sintaxe simples para a função assíncrona é:

Future Futobj = assíncrono (fn, fnargs)

A função assíncrona retorna um futuro objeto. O primeiro argumento aqui, para a função assíncrona, é o nome da função de nível superior. Pode haver mais de um argumento depois disso. O restante dos argumentos são argumentos para a função de nível superior.

Se a função de nível superior retornar um valor, esse valor será um membro do objeto futuro. E esta é uma maneira de imitar um tópico que retorna um valor.

futuro :: get ()

A função assíncrona retorna um futuro objeto. Este objeto futuro tem o valor de retorno da função que é um argumento para a função assíncrona. Para obter esse valor, a função get () do futuro objeto deve ser usada. O cenário é:

futuro futoBJ = assíncrono (fn, fnargs);
TIPO FOTOBJ.pegar();

Quando a função get () é chamada, o corpo da função de chamada aguarda (blocos), até que a função assíncrona retorne seu valor. Depois disso, o restante das declarações abaixo da declaração get () continua a executar.

O programa a seguir é o acima, onde nenhum tópico foi criado oficialmente e, no lugar da declaração de junção, a declaração get () foi usada. A função Async () foi usada para simular um tópico. As duas funções de nível superior foram reduzidas a uma. O programa é:

#incluir
#incluir
#incluir
usando namespace std;
string globl1 = string ("SO");
string globl2 = string ("Seja isso!");
string fn (string st1, string st2)
String concat = st1 + st2;
retornar concat;

int main ()

futuro futuro = assíncrono (fn, GLOBL1, GLOBL2);
String ret = FUT.pegar(); // main () espera aqui
String result = "Sim. " + ret;
cout << result << endl;
retornar 0;

Observe que a futura biblioteca, em vez da biblioteca de threads, foi incluída. A saída é:

Sim. Que assim seja!

Conclusão

Quando uma declaração de ingresso está preocupada, duas funções de nível superior estão envolvidas. Um é a função de chamada, e o outro é a função chamada. No corpo da função de chamada está a declaração de junção. Essas duas funções podem ser encapsuladas em um tópico. A função de membro junção () do encadeamento chamado está no corpo do tópico de chamada. Quando a função junção () é chamada, o thread de chamada aguarda nesse ponto (blocos) até que o encadeamento chamado seja concluído; Antes de continuar operando.

O uso de threads pode ser evitado usando a função Async () na Biblioteca Future. Esta função assume uma função de nível superior como um argumento e retorna um objeto futuro, que contém o valor retornado da função de argumento à função async (). Para obter o valor de retorno da função como um argumento, a função get () do membro do futuro deve ser usada. Quando a função get () membro é chamada, o corpo da função de chamada aguarda nesse ponto (blocos) até que a função chamada seja concluída; Antes de continuar operando.