AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / coding / Perguntas / 79591848
Accepted
user
user
Asked: 2025-04-25 13:18:16 +0800 CST2025-04-25 13:18:16 +0800 CST 2025-04-25 13:18:16 +0800 CST

Um método inline pode causar uma condição de corrida para um objeto referenciado pelo tipo de referência?

  • 772

Tenho uma variável de instância ou propriedade chamada Settingque é compartilhada entre threads.
Uma nova instância ocasionalmente será atribuída a essa variável por outras threads.
O método que usa Settingcopiará a referência antes de funcionar, e não há problema em usar a antiga Settingse ela estiver sendo atualizada quando o trabalho estiver prestes a começar, então o bloqueio não é necessário.

A leitura e a escrita únicas do tipo de referência são atômicas em C#, mas instruções compostas como var foo = new Foo()essa não são atômicas e podem ser reordenadas.
Este problema é bem ilustrado no bloqueio de dupla verificação. https://medium.com/@wangberlin2000/why-volatile-is-essential-in-double-checked-locking-singleton-0ba2906623fe
O link foi escrito em Java, mas o mesmo se aplica a C#.

https://csharpindepth.com/Articles/Singleton
Adicionado também o link para C#. Confira a "Terceira versão - tentativa de segurança de thread usando bloqueio de verificação dupla".

Gostaria de saber se é possível ler o Settingquando seu endereço de memória é alocado, mas a inicialização do objectnão é concluída.

O código de exemplo tem 3 Settingvariáveis ​​de membro de instância, propertye virtual propertyfunções que atualizam o Setting. É para fins de demonstração e o código real tem apenas uma Settinge uma função.

  1. É UpdateMemberAsyncseguro usar, já que o conteúdo GetSettingAsyncnão pode vazar antes ou depois da awaitreordenação da memória?
  2. Não é UpdateMemberByInlinableseguro usar porque GetSettingpode ser incorporado em vez de retornar o inicializado object?
  3. É UpdateVirtualMemberseguro usar porque virtual propertynão pode ser embutido?
  4. É UpdateMemberByNonInlinableseguro usar porque Apinão pode ser embutido?
  5. É UpdateMemberUsingBarrierseguro usar?
  6. Há algo que estou esquecendo além dos exemplos fornecidos?

Adicionado o link fornecido por Peter Cordes para aqueles que buscam informações mais detalhadas. https://preshing.com/20120612/an-introduction-to-lock-free-programming/

public class Service
{
    public Setting Setting = new();
    public Setting SettingProperty { get; set; } = new();
    public virtual Setting SettingVirtualProperty { get; set; } = new();

    // Called by other threads.
    public async Task StartAsync()
    {
        // It doesn't have to be the most recent value.
        var setting = Setting;

        await Foo(setting);
        await Bar(setting);
    }

    // Api is a class with a non-virtual or sealed function.
    public void UpdateMemberByInlinable(Api api)
    {
        Setting = api.GetSetting();

        // Inlined as below.
        var t = new Setting();

        Setting = ptrT;
        // or
        SettingProperty.BackingField = ptrT;

        // Set members of t after assigning the ptr.
    }

    // Api can be anything.
    public async Task UpdateMemberAsync(Api api)
    {
        Setting = await api.GetSettingAsync();
        // or
        SettingProperty = await api.GetSettingAsync();
        // or
        SettingVirtualProperty = await api.GetSettingAsync();
    }

    // Api can be anything.
    public void UpdateVirtualMember(Api api)
    {
        SettingVirtualProperty = api.GetSetting();
    }

    // Api is an interface or a class with a non-sealed virtual function.
    public void UpdateMemberByNonInlinable(Api api)
    {
        Setting = api.GetSetting();
        // or
        SettingProperty = api.GetSetting();
    }

    // Api can be anything.
    public void UpdateMemberUsingBarrier(Api api)
    {
        var setting = api.GetSetting();

        Thread.MemoryBarrier();

        Setting = setting;
        // or
        SettingProperty = setting;
    }
}
c#
  • 2 2 respostas
  • 157 Views

2 respostas

  • Voted
  1. Best Answer
    Theodor Zoulias
    2025-04-26T16:20:32+08:002025-04-26T16:20:32+08:00

    Gostaria de saber se é possível ler o Settingquando seu endereço de memória é alocado, mas a inicialização do objeto não é concluída.

    Sim, teoricamente é possível. O compilador C# tem permissão para fazer todos os tipos de reordenações que não afetam a execução em uma única thread. Se isso é praticamente possível, ou seja, se os compiladores C# existentes realmente realizam tais otimizações, é uma questão que alguns especialistas podem ser capazes de responder, mas eu não sou um deles.

    É UpdateMemberAsyncseguro usar...

    Nenhuma delas é segura para uso. Todas são inseguras. Até mesmo a UpdateMemberUsingBarrieré insegura, pois não há barreira de memória ao ler o campo compartilhado. A ausência dessa barreira torna possível, pelo menos teoricamente, que instruções após a leitura sejam reordenadas e saltem antes da leitura.

    A maneira padrão de evitar o cenário de leitura de instâncias parcialmente inicializadas é a volatilepalavra-chave . Basta anotar seu Settingcampo como volatile, e o C# adicionará as barreiras de memória apropriadas sempre que você ler ou atualizar o campo. Você pode aprender sobre as reordenações específicas que são evitadas lendo a documentação dos métodos Volatile.Reade Volatile.Write. Você também pode consultar um exemplo de uso educacional da volatilepalavra-chave que publiquei nesta resposta .

    • 1
  2. JonasH
    2025-04-25T14:58:59+08:002025-04-25T14:58:59+08:00

    A atribuição de uma referência é sempre atômica, portanto, nunca pode haver problemas com gravações distorcidas de uma referência. Isso pode ser um problema apenas em tipos de valor maiores que o tamanho do ponteiro nativo. Portanto, se Settingfor uma struct, você pode ter um problema; se for uma classe, tudo bem.

    Eu esperaria que o compilador fosse bastante restrito em relação à otimização em torno de um await. asyncOs métodos serão essencialmente reescritos em uma máquina de estados, e você precisa considerar quaisquer otimizações no contexto dessa máquina de estados. Observe que a awaitintenção é ser uma API fácil de usar para métodos assíncronos, exigindo que o usuário insira quaisquer barreiras que contrariem esse objetivo.

    Claro, mesmo que todas as operações sejam atômicas, isso não significa necessariamente que todo o programa seja thread-safe. Você ainda pode precisar de bloqueios para garantir que sequências de operações não sejam executadas simultaneamente. Mas não há exemplos disso no código publicado, até onde eu sei.

    • 0

relate perguntas

  • Polly DecorrelatedJitterBackoffV2 - como calcular o tempo máximo necessário para concluir todas as novas tentativas?

  • Wpf. Role o DataGrid dentro do ScrollViewer

  • A pontuação que ganhei na página do jogo com .NET MAUI MVVM não é visível em outras páginas. Como posso manter os dados de pontuação no dispositivo local

  • Use a hierarquia TreeView com HierarchicalDataTemplate de dentro de um DataTemplate

  • Como posso melhorar essa interface de validação no .NET?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Reformatar números, inserindo separadores em posições fixas

    • 6 respostas
  • Marko Smith

    Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não?

    • 2 respostas
  • Marko Smith

    Problema com extensão desinstalada automaticamente do VScode (tema Material)

    • 2 respostas
  • Marko Smith

    Vue 3: Erro na criação "Identificador esperado, mas encontrado 'import'" [duplicado]

    • 1 respostas
  • Marko Smith

    Qual é o propósito de `enum class` com um tipo subjacente especificado, mas sem enumeradores?

    • 1 respostas
  • Marko Smith

    Como faço para corrigir um erro MODULE_NOT_FOUND para um módulo que não importei manualmente?

    • 6 respostas
  • Marko Smith

    `(expression, lvalue) = rvalue` é uma atribuição válida em C ou C++? Por que alguns compiladores aceitam/rejeitam isso?

    • 3 respostas
  • Marko Smith

    Um programa vazio que não faz nada em C++ precisa de um heap de 204 KB, mas não em C

    • 1 respostas
  • Marko Smith

    PowerBI atualmente quebrado com BigQuery: problema de driver Simba com atualização do Windows

    • 2 respostas
  • Marko Smith

    AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos

    • 1 respostas
  • Martin Hope
    Fantastic Mr Fox Somente o tipo copiável não é aceito na implementação std::vector do MSVC 2025-04-23 06:40:49 +0800 CST
  • Martin Hope
    Howard Hinnant Encontre o próximo dia da semana usando o cronógrafo 2025-04-21 08:30:25 +0800 CST
  • Martin Hope
    Fedor O inicializador de membro do construtor pode incluir a inicialização de outro membro? 2025-04-15 01:01:44 +0800 CST
  • Martin Hope
    Petr Filipský Por que os conceitos do C++20 causam erros de restrição cíclica, enquanto o SFINAE antigo não? 2025-03-23 21:39:40 +0800 CST
  • Martin Hope
    Catskul O C++20 mudou para permitir a conversão de `type(&)[N]` de matriz de limites conhecidos para `type(&)[]` de matriz de limites desconhecidos? 2025-03-04 06:57:53 +0800 CST
  • Martin Hope
    Stefan Pochmann Como/por que {2,3,10} e {x,3,10} com x=2 são ordenados de forma diferente? 2025-01-13 23:24:07 +0800 CST
  • Martin Hope
    Chad Feller O ponto e vírgula agora é opcional em condicionais bash com [[ .. ]] na versão 5.2? 2024-10-21 05:50:33 +0800 CST
  • Martin Hope
    Wrench Por que um traço duplo (--) faz com que esta cláusula MariaDB seja avaliada como verdadeira? 2024-05-05 13:37:20 +0800 CST
  • Martin Hope
    Waket Zheng Por que `dict(id=1, **{'id': 2})` às vezes gera `KeyError: 'id'` em vez de um TypeError? 2024-05-04 14:19:19 +0800 CST
  • Martin Hope
    user924 AdMob: MobileAds.initialize() - "java.lang.Integer não pode ser convertido em java.lang.String" para alguns dispositivos 2024-03-20 03:12:31 +0800 CST

Hot tag

python javascript c++ c# java typescript sql reactjs html

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve