È meglio creare sottoclassi thread-safe o rendere sicuro il thread della classe principale?

4

Supponiamo di avere una classe frequentemente utilizzata in un contesto a thread singolo:

public class Foo
{
    public List<Bar> Data;

    public virtual void ChangeData()
    {
        for(var i =0; i < Data.Count; i++)
        {
            if(i % 4 == 0)
            {
                data[i].Baz();
            }
        }
    }
}

Se dovessimo decidere che volevamo usare il thread in un contesto multi-thread (oltre al suo attuale utilizzo a thread singolo), ci sono dei vantaggi (supponendo che abbia una copertura completa del test unitario) per creare un thread-safe sottoclasse 'ThreadSafeFoo' vs. alterando 'Foo' in thread-safe?

Mi chiedevo questo perché ci sono sottoclassi specifiche in System.Collections che devono essere usate attraverso i thread, e volevo sapere se c'era un motivo per cui questo thread-safety non era semplicemente passato in List , ecc.

    
posta Nick Udell 14.10.2014 - 22:17
fonte

2 risposte

4

I was wondering about this because there are specific subclasses in System.Collections that are intended to be used across threads, and I wanted to know whether there was a reason this thread-safety wasn't simply rolled into List, etc.

In questo esempio specifico, è perché solo un sottoinsieme molto limitato di azioni su say ... Dictionary può essere eseguito in modo performante in ConcurrentDictionary . Sottoporre tutti dei tuoi utenti al sovraccarico della sincronizzazione (o ai limiti della funzionalità) è ... sconsigliato.

Nel caso più generale, dovresti errare in generale verso l'utilizzo di una sola classe. Riduce la complessità del tuo codice. È meno codice da mantenere. Rende più facile per le persone fare "la cosa giusta". A volte è troppo oneroso fornire la sicurezza del thread e si ottengono due classi. Ma se non lo è, o è vicino, usa solo quello.

    
risposta data 14.10.2014 - 22:29
fonte
1

Il problema con questo tipo di pensiero è che spesso non si può semplicemente creare una classe sicura per i thread (almeno non in un modo che sia utile), senza modificare anche il modo in cui funziona. E le raccolte lo dimostrano bene: ad esempio, utilizzando% normaleDictionary, potresti implementare un tipo di memorizzazione nella cache come questa:

if (!dictionary.ContainsKey(key))
    dictionary.Add(key, GetValue(key));

return dictionary[key];

Ma tale codice non funzionerebbe bene in un ambiente multi-thread, anche se c'era una versione di Dictionary con ContainsKey() , Add() e l'indicizzatore che sono thread-safe.

Invece, usando ConcurrentDictionary (con non è una sottoclasse di Dictionary ), potresti scrivere lo stesso codice in questo modo:

return dictionary.GetOrAdd(key, GetValue);

Ma di solito non è necessario andare così lontano per ottenere la sicurezza del thread, il modo corretto di usare lock è sufficiente:

lock (dictionary)
{
    if (!dictionary.ContainsKey(key))
        dictionary.Add(key, GetValue(key));

    return dictionary[key];
}

Ma questo significa usare il tipo in modo thread-safe, non cambiarlo o creare una sottoclasse.

    
risposta data 15.10.2014 - 21:35
fonte

Leggi altre domande sui tag