Próbuję napisać metodę bezpieczną dla wątków, która może być wywołana tylko raz (na instancję obiektu). Wyjątek powinien zostać zgłoszony, jeśli został wcześniej wywołany.Jak: Napisz metodę bezpieczną dla wątków, która może być wywołana tylko raz?
Mam dwa rozwiązania. Czy obie są poprawne? Jeśli nie, to co jest z nimi nie tak?
Z
lock
:public void Foo() { lock (fooLock) { if (fooCalled) throw new InvalidOperationException(); fooCalled = true; } … } private object fooLock = new object(); private bool fooCalled;
Z
Interlocked.CompareExchange
:public void Foo() { if (Interlocked.CompareExchange(ref fooCalled, 1, 0) == 1) throw new InvalidOperationException(); … } private int fooCalled;
Jeśli się nie mylę, to rozwiązanie ma tę zaletę, że lock-free (co wydaje się nieistotne w moim przypadku) i że wymaga mniej prywatnych pól .
Jestem również otwarty na uzasadnione opinie, które rozwiązanie powinno być preferowane, oraz na dalsze sugestie, czy istnieje lepszy sposób.
Z ciekawości: Kiedy mówisz, że jest "mniej skomplikowany", wydajesz się odnosić do wszystkiego, co dzieje się za ciemnymi; jak oceniasz czytelność/łatwość zrozumienia konstrukcji "Interlocked.ExchangeCompare" dla przeciętnego programisty? – stakx
@stakx: po to są komentarze. Kiedy programista napotka coś, czego nie rozumie, powinien sprawdzić, czy go rozumieją. W ten sposób stają się lepszym programistą. – thecoop
@ theheop Nie zgadzam się, tak, to jest najbardziej poprawne rozwiązanie, ale nie jest to proste, musisz wiedzieć o operacjach atomowych itd. To jest jakikolwiek proces inicjalizacji i radziłbym podążać za wzorcami inicjalizacji, które są szeroko stosowane (np. sprawdzona blokada). Również te wzory uniemożliwiają utratę czegoś, co łatwo może się wydarzyć podczas wykonywania wątków. – ntziolis