Qual é o significado do termo "thread-safe"?

367

Isso significa que dois threads não podem alterar os dados subjacentes simultaneamente? Ou significa que o segmento de código fornecido será executado com resultados previsíveis quando vários threads estiverem executando esse segmento de código?

Varun Mahajan
fonte
8
Só vi uma discussão interessante aqui sobre este assunto: blogs.msdn.com/ericlippert/archive/2009/10/19/...
Sebastian

Respostas:

256

Da Wikipedia:

A segurança de threads é um conceito de programação de computador aplicável no contexto de programas multithread. Um trecho de código é seguro para threads se funcionar corretamente durante a execução simultânea de vários threads. Em particular, ele deve satisfazer a necessidade de vários encadeamentos para acessar os mesmos dados compartilhados e a necessidade de um pedaço de dados compartilhado ser acessado por apenas um encadeamento por vez.

Existem algumas maneiras de obter segurança de thread:

Reentrada:

Escrever código de forma que possa ser parcialmente executado por uma tarefa, reinserido por outra tarefa e depois retomado da tarefa original. Isso requer o salvamento de informações de estado em variáveis ​​locais para cada tarefa, geralmente em sua pilha, em vez de em variáveis ​​estáticas ou globais.

Exclusão mútua:

O acesso aos dados compartilhados é serializado usando mecanismos que garantem que apenas um encadeamento leia ou grave os dados compartilhados a qualquer momento. É necessário muito cuidado se um pedaço de código acessa vários dados compartilhados - os problemas incluem condições de corrida, conflitos, livelocks, fome e vários outros males enumerados em muitos livros de sistemas operacionais.

Armazenamento local do encadeamento:

As variáveis ​​são localizadas para que cada thread tenha sua própria cópia privada. Essas variáveis ​​mantêm seus valores nas sub-rotinas e outros limites de código e são seguros para threads, pois são locais para cada thread, mesmo que o código que os acessa possa ser reentrante.

Operações atômicas:

Os dados compartilhados são acessados ​​usando operações atômicas que não podem ser interrompidas por outros threads. Isso geralmente requer o uso de instruções especiais de linguagem de máquina, que podem estar disponíveis em uma biblioteca de tempo de execução. Como as operações são atômicas, os dados compartilhados são sempre mantidos em um estado válido, independentemente de outros threads que os acessem. As operações atômicas formam a base de muitos mecanismos de bloqueio de encadeamento.

consulte Mais informação:

http://en.wikipedia.org/wiki/Thread_safety


Blauohr
fonte
4
Tecnicamente, esse link está ausente de vários pontos críticos. A memória compartilhada em questão deve ser mutável (a memória somente leitura não pode ser insegura do thread) e os vários threads devem: a) executar várias operações de gravação na memória durante o meio no qual a memória está inconsistente (incorreta) eb) permitir que outros threads o interrompam enquanto a memória estiver inconsistente.
Charles Bretana
20
quando o primeiro resultado pesquisado no Google é wiki, não há sentido em torná-lo redundante aqui.
Ranvir
O que você quer dizer com "um código acessando uma função"? A função que está sendo executada é o código, não é?
Koray Tugay
82

Código de thread-safe é um código que funcionará mesmo que muitos Threads o estejam executando simultaneamente.

http://mindprod.com/jgloss/threadsafe.html

Marek Blotny
fonte
34
Dentro do mesmo processo!
Ali Afshar
De fato, no mesmo processo :)
Marek Blotny
4
"Escrever código que funcione de forma estável por semanas exige extrema paranóia". Essa é uma frase que eu gosto :)
Jim T
5
duh! esta resposta apenas reafirma a pergunta! --- E por que apenas dentro do mesmo processo ??? Se o código falhar quando vários threads o executam a partir de processos diferentes, então, sem dúvida, (a "memória compartilhada" pode estar em um arquivo de disco), NÃO é seguro para threads!
Charles Bretana
11
@ mg30rg. Talvez a confusão seja o resultado de, de alguma maneira, pensar que, quando um bloco de código está sendo executado por vários processos, mas apenas por um encadeamento por processo, isso ainda é um cenário de "encadeamento único", não um cenário de encadeamento múltiplo . Essa ideia nem está errada. É apenas uma definição incorreta. Claramente, múltiplos processos geralmente não executar no mesmo segmento de forma sincronizada, (exceto em cenários raros onde os processos de projeto coordenar um com o outro e as ações OS tópicos entre os processos.)
Charles Bretana
50

Uma pergunta mais informativa é o que torna o código não seguro para threads - e a resposta é que existem quatro condições que devem ser verdadeiras ... Imagine o seguinte código (e é a tradução automática)

totalRequests = totalRequests + 1
MOV EAX, [totalRequests]   // load memory for tot Requests into register
INC EAX                    // update register
MOV [totalRequests], EAX   // store updated value back to memory
  1. A primeira condição é que haja locais de memória acessíveis em mais de um thread. Normalmente, esses locais são variáveis ​​globais / estáticas ou têm memória de pilha acessível a partir de variáveis ​​globais / estáticas. Cada encadeamento obtém seu próprio quadro de pilha para variáveis ​​locais com escopo de função / método, portanto, essas variáveis ​​de função / método local, otoh, (que estão na pilha) são acessíveis apenas a partir do encadeamento que possui essa pilha.
  2. A segunda condição é que exista uma propriedade (geralmente chamada de invariável ), associada a esses locais de memória compartilhada, que deve ser verdadeira ou válida para que o programa funcione corretamente. No exemplo acima, a propriedade é que " totalRequests deve representar com precisão o número total de vezes que um encadeamento executou qualquer parte da instrução de incremento ". Normalmente, essa propriedade invariável precisa ser verdadeira (nesse caso, totalRequests deve conter uma contagem precisa) antes que ocorra uma atualização para que a atualização esteja correta.
  3. A terceira condição é que a propriedade invariável NÃO seja mantida durante alguma parte da atualização real. (É temporariamente inválido ou falso durante alguma parte do processamento). Nesse caso específico, desde o momento em que totalRequests é buscado até o momento em que o valor atualizado é armazenado, totalRequests não satisfaz a invariante.
  4. A quarta e última condição que deve ocorrer para que uma corrida aconteça (e, portanto, o código NÃO seja "seguro para threads") é que outro thread precise acessar a memória compartilhada enquanto a invariante estiver quebrada, causando assim inconsistências ou comportamento incorreto.
Charles Bretana
fonte
6
Isso cobre apenas o que é conhecido como corridas de dados e, é claro, é importante. No entanto, existem outras maneiras pelas quais o código não pode ser seguro para threads - por exemplo, bloqueio incorreto que pode levar a conflitos. Mesmo algo simples como chamar System.exit () em algum lugar de um encadeamento java torna esse código não seguro.
Ingo
2
Eu acho que até certo ponto isso é semântica, mas eu argumentaria que um código de bloqueio ruim que pode causar um impasse não torna o código inseguro. Primeiro, não há necessidade de bloquear o código em primeiro lugar, a menos que uma condição de corrida, como descrita acima, seja possível. Então, se você escrever o código de bloqueio de forma a causar um impasse, isso não é perigoso para o thread, é apenas um código incorreto.
Charles Bretana
11
Mas observe que o impasse não ocorrerá durante a execução de thread único, portanto, para a maioria de nós, isso certamente se enquadra no significado intuitivo de (não) "thread-safe".
Jon Coombs
Bem, os conflitos não podem ocorrer, a menos que você esteja executando vários threads, é claro, mas é como dizer que problemas de rede não podem acontecer se você estiver executando em uma máquina. Outros problemas também podem ocorrer com thread único, se o programador gravar o código para que ele rompa as linhas críticas de código antes de concluir a atualização e modifique a variável em outra sub-rotina.
Charles Bretana
34

Gosto da definição da Concorrência Java na Prática de Brian Goetz por sua abrangência

"Uma classe é segura para threads se se comportar corretamente quando acessada de vários threads, independentemente do agendamento ou intercalação da execução desses threads pelo ambiente de tempo de execução, e sem sincronização adicional ou outra coordenação por parte do código de chamada. "

Buu Nguyen
fonte
Esta definição é incompleta e não específica, e definitivamente não abrangente. Quantas vezes deve ser executado com segurança, apenas uma vez? dez vezes? toda vez? 80% do tempo? e não especifica o que o torna "inseguro". Se ele falha ao executar com segurança, mas a falha ocorreu porque existe um erro de divisão por zero, isso faz com que o thread seja "inseguro"?
Charles Bretana
Seja mais civilizado da próxima vez e talvez possamos discutir. Este não é o Reddit e não estou com disposição para conversar com pessoas rudes.
Buu Nguyen
Seus comentários de interpretação sobre a definição de alguém como insulto a si mesmo são reveladores. Você precisa ler e entender a substância antes de reagir emocionalmente. Nada de inculto no meu comentário. Eu estava fazendo uma observação sobre o significado da definição. Desculpe se os exemplos que usei para ilustrar o argumento o deixaram desconfortável.
Charles Bretana
28

Como outros já apontaram, a segurança do encadeamento significa que um código funcionará sem erros se for usado por mais de um encadeamento de uma só vez.

Vale a pena estar ciente de que isso às vezes tem um custo, tempo de computador e codificação mais complexa, portanto nem sempre é desejável. Se uma classe puder ser usada com segurança em apenas um thread, talvez seja melhor fazê-lo.

Por exemplo, Java possui duas classes quase equivalentes StringBuffere StringBuilder. A diferença é que StringBufferé seguro para threads, portanto, uma única instância de a StringBufferpode ser usada por vários threads ao mesmo tempo. StringBuildernão é seguro para threads e foi projetado como um substituto de alto desempenho para esses casos (a grande maioria) quando a String é criada por apenas um thread.

Marcus Downing
fonte
22

O código de segurança de thread funciona como especificado, mesmo quando inserido simultaneamente por diferentes threads. Isso geralmente significa que estruturas de dados internas ou operações que devem ser executadas ininterruptamente são protegidas contra diferentes modificações ao mesmo tempo.

Mnementh
fonte
21

Uma maneira mais fácil de entender isso é o que torna o código não seguro para threads. Há dois problemas principais que farão com que um aplicativo encadeado tenha comportamento indesejado.

  • Acessando variável compartilhada sem bloqueio
    Esta variável pode ser modificada por outro encadeamento durante a execução da função. Você deseja evitá-lo com um mecanismo de bloqueio para garantir o comportamento de sua função. A regra geral é manter a trava pelo menor tempo possível.

  • Impasse causado pela dependência mútua da variável compartilhada
    Se você possui duas variáveis ​​compartilhadas A e B. Em uma função, você bloqueia A primeiro e depois depois bloqueia B. Em outra função, você começa a bloquear B e, depois de um tempo, bloqueia A. é um impasse em potencial, onde a primeira função aguardará o desbloqueio de B quando a segunda função aguardará o desbloqueio de A. Esse problema provavelmente não ocorrerá no seu ambiente de desenvolvimento e apenas periodicamente. Para evitá-lo, todos os bloqueios devem sempre estar na mesma ordem.

Hapkido
fonte
9

Sim e não.

A segurança do encadeamento é um pouco mais do que apenas garantir que seus dados compartilhados sejam acessados ​​apenas por um encadeamento por vez. Você deve garantir o acesso seqüencial aos dados compartilhados, evitando ao mesmo tempo condições de corrida , deadlocks , livelocks e falta de recursos .

Resultados imprevisíveis quando vários encadeamentos estão em execução não são uma condição necessária do código de segurança de encadeamentos, mas geralmente são um subproduto. Por exemplo, você pode ter um esquema produtor-consumidor configurado com uma fila compartilhada, um encadeamento de produtor e poucos encadeamentos de consumidor, e o fluxo de dados pode ser perfeitamente previsível. Se você começar a apresentar mais consumidores, verá mais resultados de aparência aleatória.

Bill the Lizard
fonte
9

Em essência, muitas coisas podem dar errado em um ambiente multiencadeado (reordenação de instruções, objetos parcialmente construídos, mesma variável com valores diferentes em encadeamentos diferentes devido ao armazenamento em cache no nível da CPU etc.).

Gosto da definição dada pelo Java Concurrency in Practice :

Uma [parte do código] é segura para threads se se comportar corretamente quando acessada de vários threads, independentemente do agendamento ou intercalação da execução desses threads pelo ambiente de tempo de execução e sem sincronização adicional ou outra coordenação por parte do código de chamada.

Por corretamente, eles significam que o programa se comporta de acordo com suas especificações.

Exemplo artificial

Imagine que você implementa um contador. Você poderia dizer que se comporta corretamente se:

  • counter.next() nunca retorna um valor que já foi retornado antes (assumimos que não há excesso, etc., por simplicidade)
  • todos os valores de 0 ao valor atual foram retornados em algum estágio (nenhum valor é ignorado)

Um contador seguro de thread se comportaria de acordo com essas regras, independentemente de quantos threads o acessem simultaneamente (o que normalmente não seria o caso de uma implementação ingênua).

Nota: postagem cruzada em programadores

assylias
fonte
8

O Simply - code funcionará bem se muitos threads estiverem executando esse código ao mesmo tempo.

Greg Balajewicz
fonte
5

Não confunda segurança de linha com determinismo. Código de thread-safe também pode ser não determinístico. Dada a dificuldade de depurar problemas com código encadeado, esse é provavelmente o caso normal. :-)

A segurança do encadeamento simplesmente garante que, quando um encadeamento estiver modificando ou lendo dados compartilhados, nenhum outro encadeamento possa acessá-lo de maneira a alterar os dados. Se o seu código depender de uma determinada ordem de execução para correção, você precisará de outros mecanismos de sincronização além daqueles necessários para a segurança do encadeamento para garantir isso.

tvanfosson
fonte
5

Gostaria de adicionar mais algumas informações sobre outras boas respostas.

A segurança do encadeamento implica que vários encadeamentos podem gravar / ler dados no mesmo objeto sem erros de inconsistência de memória. No programa altamente multiencadeado, um programa seguro para encadeamentos não causa efeitos colaterais nos dados compartilhados .

Dê uma olhada nesta pergunta SE para obter mais detalhes:

O que significa threadsafe?

O programa seguro de thread garante consistência de memória .

Na página de documentação da Oracle na API simultânea avançada:

Propriedades de consistência de memória:

O Capítulo 17 da Especificação de Linguagem Java ™ define a relação entre o que acontece antes nas operações de memória, como leituras e gravações de variáveis ​​compartilhadas. Os resultados de uma gravação por um encadeamento são garantidos para serem visíveis para uma leitura por outro encadeamento apenas se a operação de gravação ocorrer antes da operação de leitura .

As construções synchronizede volatile, assim como os métodos Thread.start()e Thread.join(), podem formar relacionamentos anteriores ao acontecimento.

Os métodos de todas as classes java.util.concurrente seus subpacotes estendem essas garantias à sincronização de nível superior. Em particular:

  1. As ações em um encadeamento antes de colocar um objeto em qualquer coleção simultânea acontecem antes das ações subseqüentes ao acesso ou remoção desse elemento da coleção em outro encadeamento.
  2. Ações em um encadeamento anterior ao envio de a Runnablepara um Executoracontecimento - antes de sua execução começar. Da mesma forma para Callables submetidos a um ExecutorService.
  3. Ações executadas pela computação assíncrona representada por uma Futureação anterior ao processo subsequente à recuperação do resultado por meio Future.get()de outro encadeamento.
  4. Ações anteriores à "liberação" de métodos do sincronizador , como Lock.unlock, Semaphore.release, and CountDownLatch.countDownações anteriores a um método de "aquisição" bem-sucedido, como Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.awaitno mesmo objeto do sincronizador em outro encadeamento.
  5. Para cada par de encadeamentos que trocam objetos com êxito por meio de um Exchanger, as ações anteriores ao exchange()em cada encadeamento acontecem - antes daquelas subsequentes à troca correspondente () em outro encadeamento.
  6. As ações anteriores à chamada CyclicBarrier.awaite Phaser.awaitAdvance(bem como suas variantes) acontecem antes das ações executadas pela ação de barreira, e as ações executadas pela ação de barreira acontecem antes das ações subsequentes a um retorno bem-sucedido da espera correspondente em outros encadeamentos.
Ravindra babu
fonte
4

Para completar outras respostas:

A sincronização é apenas uma preocupação quando o código no seu método faz uma de duas coisas:

  1. funciona com algum recurso externo que não é seguro para threads.
  2. Lê ou altera um objeto ou campo de classe persistente

Isso significa que as variáveis ​​definidas DENTRO do seu método são sempre protegidas por thread. Toda chamada para um método tem sua própria versão dessas variáveis. Se o método for chamado por outro encadeamento, ou pelo mesmo encadeamento, ou mesmo se o método se chamar (recursão), os valores dessas variáveis ​​não serão compartilhados.

O agendamento de encadeamentos não é garantido como rodízio . Uma tarefa pode monopolizar totalmente a CPU às custas de threads da mesma prioridade. Você pode usar Thread.yield () para ter consciência. Você pode usar (em java) Thread.setPriority (Thread.NORM_PRIORITY-1) para diminuir a prioridade de um thread

Além disso, cuidado com:

  • o grande custo de tempo de execução (já mencionado por outros) em aplicativos que repetem essas estruturas "seguras para threads".
  • O thread.sleep (5000) deve dormir por 5 segundos. No entanto, se alguém alterar a hora do sistema, você poderá dormir por um período muito longo ou sem tempo. O sistema operacional registra o tempo de ativação em forma absoluta, não relativa.
VonC
fonte
2

Sim e sim. Isso implica que os dados não são modificados por mais de um thread simultaneamente. No entanto, seu programa pode funcionar conforme o esperado e parecer seguro para threads, mesmo que não seja fundamentalmente.

Observe que a imprevisibilidade dos resultados é uma consequência das 'condições da corrida' que provavelmente resultam na modificação dos dados em uma ordem diferente da esperada.

Steve Knight
fonte
2

Vamos responder isso por exemplo:

class NonThreadSafe {

    private int counter = 0;

    public boolean countTo10() {
        count = count + 1;
        return (count == 10);
    }

O countTo10método adiciona um ao contador e retorna true se a contagem tiver atingido 10. Ele deve retornar true apenas uma vez.

Isso funcionará enquanto apenas um thread estiver executando o código. Se dois threads executam o código ao mesmo tempo, vários problemas podem ocorrer.

Por exemplo, se count começar como 9, um thread poderá adicionar 1 para contar (10), mas um segundo thread poderá inserir o método e adicionar 1 novamente (11) antes que o primeiro thread possa executar a comparação com 10 Então os dois threads fazem a comparação e descobrem que a contagem é 11 e nenhum retorna verdadeiro.

Portanto, esse código não é seguro para threads.

Em essência, todos os problemas de multiencadeamento são causados ​​por alguma variação desse tipo de problema.

A solução é garantir que a adição e a comparação não possam ser separadas (por exemplo, envolvendo as duas instruções por algum tipo de código de sincronização) ou criando uma solução que não exija duas operações. Esse código seria seguro para threads.

rghome
fonte
1

Pelo menos em C ++, penso em thread-safe como um pouco impróprio, pois deixa muito a desejar. Para ser seguro para threads, o código geralmente precisa ser proativo . Geralmente não é uma qualidade passiva.

Para que uma classe seja segura, é necessário ter recursos "extras" que aumentam a sobrecarga. Esses recursos fazem parte da implementação da classe e, de um modo geral, ocultos da interface. Ou seja, threads diferentes podem acessar qualquer membro da classe sem ter que se preocupar em entrar em conflito com um acesso simultâneo por um thread diferente E podem fazê-lo de uma maneira muito preguiçosa, usando o estilo de codificação humano comum, sem precisar fazer isso. todas essas coisas loucas de sincronização que já estão reunidas nas entranhas do código que está sendo chamado.

E é por isso que algumas pessoas preferem usar o termo sincronizado internamente .

Conjuntos de terminologia

Existem três conjuntos principais de terminologias para essas idéias que encontrei. O primeiro e historicamente mais popular (mas pior) é:

  1. discussão segura
  2. não seguro

O segundo (e melhor) é:

  1. prova de rosca
  2. linha compatível
  3. thread hostil

Um terço é:

  1. sincronizado internamente
  2. sincronizado externamente
  3. não sincronizável

Analogies

thread safe ~ prova de thread ~ sincronizada internamente

Um exemplo de sistema sincronizado internamente (também conhecido como thread-safe ou thread proof ) é um restaurante em que um host o recebe na porta e o impede de fazer fila. O anfitrião faz parte do mecanismo do restaurante para lidar com vários clientes e pode usar alguns truques bastante difíceis para otimizar o número de clientes em espera, como levar em consideração o tamanho de sua parte ou quanto tempo eles parecem ter. , ou até mesmo fazer reservas por telefone. O restaurante é sincronizado internamente porque tudo isso faz parte da interface para interagir com ele.

não é seguro para thread (mas agradável) ~ compatível com thread ~ sincronizado externamente ~ com thread livre

Suponha que você vá ao banco. Existe uma linha, ou seja, contenda para os caixas do banco. Por não ser selvagem, você reconhece que a melhor coisa a fazer no meio de uma disputa por um recurso é fazer fila como um ser civilizado. Ninguém tecnicamente faz você fazer isso. Esperamos que você tenha a programação social necessária para fazer isso por conta própria. Nesse sentido, o lobby do banco é sincronizado externamente. Devemos dizer que é inseguro? isso é o que a implicação é que se você ir com o thread-safe , thread-inseguro conjunto terminologia bipolar. Não é um conjunto muito bom de termos. A melhor terminologia é sincronizada externamente,O lobby do banco não é hostil ao acesso de vários clientes, mas também não faz o trabalho de sincronizá-los. Os clientes fazem isso eles mesmos.

Isso também é chamado de "encadeamento livre", onde "livre" é como "livre de piolhos" - ou, nesse caso, bloqueios. Bem, mais precisamente, primitivas de sincronização. Isso não significa que o código possa ser executado em vários threads sem essas primitivas. Significa apenas que ele não vem com eles já instalados e cabe a você, usuário do código, instalá-los como desejar. A instalação de suas próprias primitivas de sincronização pode ser difícil e requer muita reflexão sobre o código, mas também pode levar ao programa mais rápido possível, permitindo que você personalize como o programa é executado nas CPUs com hyperthread de hoje.

thread não seguro (e ruim) ~ thread hostil ~ não sincronizável

Um exemplo da analogia cotidiana de um sistema hostil ao segmento é um idiota com um carro esportivo que se recusa a usar os pisca-piscas e muda de faixa à vontade. O estilo de condução deles é hostil ou não sincronizável, porque você não tem como coordenar com eles, e isso pode levar à disputa pela mesma faixa, sem resolução e, portanto, um acidente quando dois carros tentam ocupar o mesmo espaço, sem nenhum protocolo para impeça isso. Esse padrão também pode ser pensado de maneira mais ampla como anti-social, o que eu prefiro porque é menos específico para threads e, portanto, mais geralmente aplicável a muitas áreas da programação.

Por que thread safe et al. é um conjunto de terminologia ruim

O primeiro e o mais antigo conjunto de terminologia falha ao fazer a distinção mais fina entre hostilidade do thread e compatibilidade de threads . A compatibilidade do encadeamento é mais passiva que a chamada segurança de encadeamento, mas isso não significa que o código chamado não seja seguro para o uso simultâneo de encadeamentos. Isso significa apenas que é passivo a sincronização que permitiria isso, transferindo-o para o código de chamada, em vez de fornecê-lo como parte de sua implementação interna. Compatível com thread é como o código provavelmente deve ser escrito por padrão na maioria dos casos, mas isso também é muitas vezes considerado erroneamente como thread inseguro, como se fosse inerentemente antissegurança, que é um ponto importante de confusão para os programadores.

NOTA: Muitos manuais de software realmente usam o termo "thread-safe" para se referir a "thread-compatible", adicionando ainda mais confusão ao que já era uma bagunça! Eu evito o termo "thread-safe" e "thread-inseguro" a todo custo por esse motivo, pois algumas fontes chamam algo de "thread-safe", enquanto outras o chamam de "thread-safe" porque não podem concordar se você precisa cumprir alguns padrões extras de segurança (primitivas de sincronização) ou NÃO ser hostil para ser considerado "seguro". Portanto, evite esses termos e use os termos mais inteligentes para evitar falhas de comunicação perigosas com outros engenheiros.

Lembrete de nossos objetivos

Essencialmente, nosso objetivo é subverter o caos.

Fazemos isso criando sistemas determinísticos nos quais podemos confiar. O determinismo é caro, principalmente devido aos custos de oportunidade de perda de paralelismo, pipelining e reordenação. Tentamos minimizar a quantidade de determinismo necessário para manter nossos custos baixos, além de evitar tomar decisões que corroerão ainda mais o pouco determinismo que podemos pagar.

A sincronização de threads é sobre aumentar a ordem e diminuir o caos. Os níveis nos quais você faz isso correspondem aos termos mencionados acima. O nível mais alto significa que um sistema se comporta de uma maneira totalmente previsível todas as vezes. O segundo nível significa que o sistema se comporta bem o suficiente para que o código de chamada possa detectar de forma confiável imprevisibilidade. Por exemplo, uma ativação espúria em uma variável de condição ou uma falha ao bloquear um mutex porque não está pronto. O terceiro nível significa que o sistema não se comporta bem o suficiente para jogar com mais ninguém e só pode ser executado SEMPRE com uma única thread sem incorrer em caos.

Daniel Russell
fonte
1

Em vez de pensar em código ou classes como thread-safe ou não, acho mais útil pensar em ações como sendo thread-safe. Duas ações são seguras para encadeamento se elas se comportarem conforme especificado quando executadas em contextos de encadeamento arbitrários. Em muitos casos, as classes suportam algumas combinações de ações de maneira segura para threads e outras não.

Por exemplo, muitas coleções, como listas de matrizes e conjuntos de hash, garantirão que, se forem acessadas inicialmente exclusivamente com um encadeamento e nunca forem modificadas depois que uma referência se tornar visível para outros encadeamentos, elas poderão ser lidas de maneira arbitrária por qualquer combinação de threads sem interferência.

O mais interessante é que algumas coleções de conjuntos de hash, como a original não genérica no .NET, podem oferecer uma garantia, desde que nenhum item seja removido e desde que apenas um thread as escreva, qualquer thread que tente ler a coleção se comportará como se estivesse acessando uma coleção em que as atualizações possam atrasar e ocorrer em ordem arbitrária, mas que, de outra forma, se comportarão normalmente. Se o segmento 1 adicionar X e, em seguida, Y, e o segmento 2 procurar e ver Y e, em seguida, X, seria possível que o segmento 2 visse que Y existe, mas X não; se esse comportamento é ou não "seguro para threads" dependerá de se o thread 2 está preparado para lidar com essa possibilidade.

Como observação final, algumas classes - especialmente o bloqueio de bibliotecas de comunicações - podem ter um método "close" ou "Dispose" que é seguro para threads em relação a todos os outros métodos, mas nenhum outro método que seja seguro para threads em relação a entre si. Se um encadeamento executar uma solicitação de leitura de bloqueio e um usuário do programa clicar em "cancelar", não haverá como uma solicitação de fechamento ser emitida pelo encadeamento que está tentando executar a leitura. A solicitação de fechamento / descarte, no entanto, pode definir de forma assíncrona um sinalizador que fará com que a solicitação de leitura seja cancelada o mais rápido possível. Depois que o fechamento é realizado em qualquer encadeamento, o objeto se torna inútil e todas as tentativas de ações futuras falham imediatamente,

supercat
fonte
0

Em palavras mais simples: P Se é seguro executar vários threads em um bloco de código, é seguro para threads *

*As condições se aplicam

As condições são mencionadas por outras respostas como 1. O resultado deve ser o mesmo se você executar um thread ou vários threads sobre ele etc.

gasto
fonte