C ++ std atômico

C ++ std atômico

Usamos o "std atomic" se queremos manter a atomicidade da operação em c++. Atomicidade é o conceito com o qual lidamos quando estamos trabalhando em operações/aplicações multithreading, onde mesmo as funções simples, como ler e escrever na execução simultaneamente, podem criar problemas ou comportamentos indefinidos no código. Para lidar com essa situação, o C ++ definiu a biblioteca “STD :: Atomic” que garante uma consistência que é seqüencial o suficiente para a execução de leitura e escrita para vários objetos distintos que descrevem o comportamento bem definido. Se um tópico estiver escrevendo em algum momento, o outro tópico está lendo naquele momento.

Procedimento:

Este artigo explorará o que é atomicidade e como podemos usar os conceitos de std atomic para lidar com os comportamentos indefinidos em nossos códigos. Discutiremos as várias funções do std atomic e implementaremos os vários exemplos para o std atomic. A função sob a DST atômica que implementaremos nos diferentes exemplos é dada da seguinte maneira:

  • Leitura e escrita mais simples de valores
  • Libere e adquirir pedidos com memória (modelo)
  • Modelo de troca
  • Operação de busca

Leitura e escrita mais simples de valores

Vamos criar um aplicativo multithread neste exemplo em que criamos dois threads: um tópico para ler os valores e o outro tópico para escrever os valores. Com a ajuda deste exemplo, tentaremos obter o conceito de std atomics, quais são os comportamentos indefinidos enquanto executam os aplicativos multithread e como a DST atômica elimina os comportamentos indefinidos.

Para isso, simplesmente iniciamos o código atribuindo dois valores diferentes a duas variáveis ​​diferentes do tipo inteiro. Primeiro, inicializamos a variável "A" e "B" com os tipos de dados inteiros. Em seguida, criamos a função que está escrita no vazio. Nesta função, atribuímos os valores a "a" e "b", e.g. 25 e 20, respectivamente.

Então, criamos a função de leitura. Na função de leitura, os valores de "a" e "b" são lidos com o "std :: cout <

Saída:

A saída deste exemplo mostra o comportamento indefinido do aplicativo, pois a saída do código é 0 ou 10. Isso aconteceu porque os tópicos estavam executando simultaneamente e o comando de leitura poderia ter sido feito durante a execução do comando de gravação. Dessa forma, obtivemos um resultado incompleto na saída.

O STD Atomic pode resolver esse problema e pode fazer com que os comportamentos indefinidos no aplicativo sejam bem definidos. Para implementar isso, simplesmente fazemos uma pequena alteração ao inicializar e definir os valores e os tipos de dados das variáveis ​​definidas usando “std :: atomic”. Definimos a variável "A" e "B" como as variáveis ​​atômicas por "Nome da variável atômica STD :: Atomic". Também fazemos uma pequena mudança na função de gravação em que, anteriormente, simplesmente atribuímos os valores a A e B usando o operador de atribuição "=". Mas aqui, atribuímos os valores usando o “Nome da variável. Método da loja (valor) ”. Usamos o “nome variável. load () ”na função de leitura. O resto é o mesmo que no exemplo anterior.

Os valores na saída são lidos e escritos de uma maneira bem definida. E a multithreading também é suportada aqui.

Libere e adquirir pedidos com memória (modelo)

O modelo de memória pode ter um enorme impacto nas funções de leitura e gravação do std atomic. O modelo de memória é uma função padrão que garante consistência sequencial de pedidos. Um dos modelos atômicos mais interessantes é o modelo de liberação e aquisição, onde podemos armazenar a liberação de pedidos de memória para o primeiro thread e a ordem de memória adquirida para o segundo tópico, o que significa que qualquer armazenamento/gravação atômico ou não atômico é feito primeiro no primeiro tópico antes do segundo tópico, eu.e. carregar.

Portanto, no exemplo, podemos até alterar a variável atômica "A" para não atômica e a segunda variável "B" é mantida atômica. Na função de gravação, armazenamos a variável não atômica "A" simplesmente atribuindo a ela qualquer valor, e.g. 30. E armazenamos adequadamente o valor da variável atômica "B" usando "B. Store (Valor, STD :: Mememy_order_release) ”. O mesmo é feito na função de leitura também onde usamos o “std :: cout <

Saída:

A atomicidade da operação ainda é mantida com o modelo de release e memória adquirida, mesmo quando tivemos uma variável x não atômica. Isso aconteceu por causa dos y's (atômico.armazenar) que garantiu a manutenção da consistência seqüencial.

Modelo de troca

Troca significa quando trocamos o valor de uma variável (atômica) a outro valor. Em troca, o valor é trocado primeiro e depois o valor anterior que está sendo trocado pelo novo é devolvido. Depois que o valor é trocado, reflete sobre todas as operações subsequentes a esse valor. Vamos implementar essa troca de variáveis ​​atômicas com a ajuda de um exemplo.

Neste exemplo, primeiro introduzimos a variável atômica global Foobar, que tem algum valor igual a "15". Em geral, fazemos um thread como Thread1 e atribuímos um valor inteiro igual a 2. Então, no loop for, definimos o índice de 0 a 100 vezes. Em seguida, substituímos o valor da variável foobar para 2 usando “foobar. Exchange (valor) ”. Depois disso, saímos do loop e carregamos o valor da variável foobar para imprimi -lo. Depois de carregar o valor de foobar, agora trocamos seu valor com 18 pelo “.Exchange (valor a ser substituído) ”Método. E, novamente, carregue os valores do foobar e os exiba usando o método de impressão.

Aqui neste exemplo, o tópico deve trocar os valores por centenas de vezes e o valor de Foobar é trocado de 15 a 28. Qualquer operação após esta troca retorna o mesmo valor que pode ser visto na saída.

Buscar

Buscar é o mesmo que a função de troca que escreve os valores e retorna os valores previamente buscados. Esta operação busca o valor que é armazenado antes de qualquer operação ser aplicada a ela. Agora, implementamos a busca de add e busca e buscar neste exemplo. Definimos uma variável atômica com o tipo de dados não assinado como “contagem” e inicializamos a contagem com zero. Em seguida, criamos duas funções - uma para buscar add e outra para buscar subtração. Executamos o balcão de incremento 1 para add e decrementos 1 para subtrair em ambas as funções. Em seguida, imprimimos esses valores das funções fetch_add e fetch_sub.

A função fetch_add retornou 0 e 1 como os valores anteriores antes do incremento. Da mesma forma, o Fetch_sub retornou 2 e 1 como os valores armazenados anteriormente antes da subtração ou decréscimo de um.

Conclusão

Implementamos as operações básicas em "std :: atomic" neste artigo. Aprendemos como podemos lidar com os problemas em aplicativos multithreading usando o std atomic. Implementamos os vários exemplos em C ++ para as diferentes funções, como buscar, troca, leitura/escrita e modelo de memória do std atomic para garantir a consistência seqüencial e comportamentos bem definidos do código para aplicativos multithread.