Sto tentando di scrivere un metodo thread-safe che può essere chiamato solo una volta (per istanza di oggetto). Un'eccezione dovrebbe essere lanciata se è stata chiamata prima.Procedura: scrivere un metodo thread-safe che può essere chiamato una sola volta?
Ho trovato due soluzioni. Sono entrambi corretti? In caso contrario, cosa c'è di sbagliato in loro?
Con
lock
:public void Foo() { lock (fooLock) { if (fooCalled) throw new InvalidOperationException(); fooCalled = true; } … } private object fooLock = new object(); private bool fooCalled;
Con
Interlocked.CompareExchange
:public void Foo() { if (Interlocked.CompareExchange(ref fooCalled, 1, 0) == 1) throw new InvalidOperationException(); … } private int fooCalled;
Se non mi sbaglio, questa soluzione ha il vantaggio di essere senza blocchi (che sembra irrilevante nel mio caso) e che richiede meno campi privati .
io sono aperto anche a pareri motivati quale soluzione dovrebbe essere preferito, e per ulteriori suggerimenti se c'è un modo migliore.
Per curiosità: quando dici che è "meno complicato", ti sembra di riferirti a tutto ciò che accade dietro i bui; come giudichi la leggibilità/facilità di comprensione del costrutto 'Interlocked.ExchangeCompare' per un programmatore medio? – stakx
@stakx: ecco a cosa servono i commenti. Quando un programmatore incontra qualcosa che non capisce, dovrebbe cercarlo in modo che lo capisca. È così che diventano un programmatore migliore. – thecoop
@thecoop Non sono d'accordo, sì è la soluzione più corretta, ma non è semplice, è necessario conoscere le operazioni atomiche ecc. Questo è in qualche modo un processo di inizializzazione e consiglierei di seguire i modelli di inizializzazione che sono ampiamente utilizzati (es. blocco selezionato). Inoltre, questi schemi ti impediscono di perdere qualcosa che può facilmente accadere quando si fa il threading. – ntziolis