Condividi tramite


Thread safety e gestione del ciclo di vita dei client per gli oggetti di Azure SDK

Questo articolo illustra i problemi di thread safety quando si usa Azure SDK. Illustra anche il modo in cui la progettazione dell'SDK influisce sulla gestione del ciclo di vita dei client. Si apprenderà perché non è necessario eliminare gli oggetti client di Azure SDK.

Thread safety

Tutti gli oggetti client di Azure SDK sono thread-safe e indipendenti l'uno dall'altro. Questa progettazione garantisce che il riutilizzo delle istanze client sia sempre sicuro, anche tra thread. Ad esempio, il codice seguente avvia più attività, ma è thread-safe:

var client = new SecretClient(
    new Uri("<secrets_endpoint>"), new DefaultAzureCredential());

foreach (var secretName in secretNames)
{
    // Using clients from parallel threads
    Task.Run(() => Console.WriteLine(client.GetSecret(secretName).Value));
}

Gli oggetti modello usati dai client SDK, che si tratti di modelli di input o output, non sono thread-safe per impostazione predefinita. La maggior parte dei casi d'uso che coinvolgono oggetti modello usa solo un singolo thread. Di conseguenza, il costo di implementazione della sincronizzazione come comportamento predefinito è troppo elevato per questi oggetti. Il codice seguente illustra un bug in cui l'accesso a un modello da più thread può causare un comportamento non definito:

KeyVaultSecret newSecret = client.SetSecret("secret", "value");

foreach (var tag in tags)
{
    // Don't use model type from parallel threads
    Task.Run(() => newSecret.Properties.Tags[tag] = CalculateTagValue(tag));
}

client.UpdateSecretProperties(newSecret.Properties);

Per accedere al modello da thread diversi, è necessario implementare il proprio codice di sincronizzazione. Ad esempio:

KeyVaultSecret newSecret = client.SetSecret("secret", "value");

// Code omitted for brevity

foreach (var tag in tags)
{
    Task.Run(() =>
    {
        lock (newSecret)
        {
            newSecret.Properties.Tags[tag] = CalculateTagValue(tag);
        }
    );
}

client.UpdateSecretProperties(newSecret.Properties);

Ciclo di vita dei client

Poiché i client Azure SDK sono thread-safe, non esiste alcun motivo per costruire più oggetti client SDK per un determinato set di parametri del costruttore. Dopo la costruzione, occorre considerare gli oggetti client di Azure SDK come singleton. Questa raccomandazione viene in genere implementata registrando gli oggetti client di Azure SDK come singleton nel contenitore IoC (Inversion of Control) dell'app. L'inserimento delle dipendenze viene usato per ottenere riferimenti all'oggetto client SDK. L'esempio seguente mostra una registrazione dell'oggetto client singleton:

var builder = Host.CreateApplicationBuilder(args);

var endpoint = builder.Configuration["SecretsEndpoint"];
var blobServiceClient = new BlobServiceClient(
    new Uri(endpoint), new DefaultAzureCredential());

builder.Services.AddSingleton(blobServiceClient);

Per altre informazioni sull'implementazione dell'inserimento delle dipendenze con Azure SDK, vedere Inserimento delle dipendenze con Azure SDK per .NET.

In alternativa, è possibile creare un'istanza del client SDK e fornirla ai metodi che richiedono un client. L'aspetto importante è evitare istanze non necessarie dello stesso oggetto client SDK con gli stessi parametri. Si tratta infatti di un approccio superfluo e dispendioso.

I client non sono eliminabili

Due domande finali che spesso si presentano sono:

  • È necessario eliminare gli oggetti client di Azure SDK al termine dell'uso?
  • Perché gli oggetti client di Azure SDK basati su HTTP non sono eliminabili?

Internamente tutti i client di Azure SDK usano una singola istanza condivisa di HttpClient. I client non creano altre risorse che devono essere liberate attivamente. L'istanza condivisa di HttpClient viene mantenuta per l'intero ciclo di vita dell'applicazione.

// Both clients reuse the shared HttpClient and don't need to be disposed
var blobClient = new BlobClient(new Uri(sasUri));
var blobClient2 = new BlobClient(new Uri(sasUri2));

È possibile fornire un'istanza personalizzata di HttpClient a un oggetto client di Azure SDK. In questo caso, si diventa responsabili della gestione del ciclo di vita di HttpClient e della corretto eliminazione al momento giusto.

var httpClient = new HttpClient();

var clientOptions = new BlobClientOptions()
{
    Transport = new HttpClientTransport(httpClient)
};

// Both clients would use the HttpClient instance provided in clientOptions
var blobClient = new BlobClient(new Uri(sasUri), clientOptions);
var blobClient2 = new BlobClient(new Uri(sasUri2), clientOptions);

// Code omitted for brevity

// You're responsible for properly disposing httpClient some time later
httpClient.Dispose();

Altre indicazioni per gestire ed eliminare correttamente le istanze di HttpClient sono disponibili nella documentazione di HttpClient.

Vedi anche