2009-08-29 18 views
5

OK, pytanie początkujących wielowątkowych:Pytanie wielowątkowe - dodawanie elementu do listy statycznej

Mam lekcję Singleton. Klasa ma statyczną listę i zasadniczo działa tak:

class MyClass { 
    private static MyClass _instance; 
    private static List<string> _list; 
    private static bool IsRecording; 

    public static void StartRecording() { 
     _list = new List<string>(); 
     IsRecording = true; 
    } 

    public static IEnumerable<string> StopRecording() { 
     IsRecording = false; 
     return new List<string>(_list).AsReadOnly(); 
    } 

    public MyClass GetInstance(){ 

    } 

    public void DoSomething(){ 
     if(IsRecording) _list.Add("Something"); 
    } 
} 

Zasadniczo użytkownik może zadzwonić StartRecording(), aby zainicjować listy, a następnie wszystkie połączenia do metody instancji może dodać rzeczy do listy. Jednak wiele wątków może przechowywać instancję w MyClass, więc wiele wątków może dodawać wpisy do listy.

Jednak tworzenie i odczyt list są pojedynczymi operacjami, więc zwykły problem z pisarzem i pisarzem w sytuacjach wielowątkowych nie ma zastosowania. Jedyny problem, jaki mogłem zauważyć, to kolejność wstawiania, która jest dziwna, ale to nie jest problem.

Czy mogę pozostawić kod bez zmian, czy też muszę przedsięwziąć środki ostrożności w przypadku wielowątkowości? Powinienem dodać, że w prawdziwej aplikacji nie jest to lista łańcuchów, ale lista obiektów niestandardowych (tak więc kod jest _list.Add (nowy obiekt (somedata))), ale te obiekty przechowują tylko dane, bez kodu poza wywołaniem do DateTime.Now.

Edit: Wyjaśnienia po kilka odpowiedzi: DoSomething nie może być statyczny (klasa jest tu w skrócie, jest wiele rzeczy dzieje się, że jest za pomocą instancji zmiennych, ale te stworzone przez konstruktora, a potem tylko do odczytu) . Czy jest wystarczająco dobry, aby zrobić

lock(_list){ 
    _list.Add(something); 
} 

and 

lock(_list){ 
    return new List<string>(_list).AsReadOnly(); 
} 

czy muszę jakąś głębszą magię?

+0

Czy to nie jest trochę dziwne być mieszające statyki i samotnymi? Czy nie powinieneś po prostu zachować swojej kolekcji w instancji? – Will

+0

Jest i rozważam, aby wszystko było statyczne. Chciałem zwięzłej składni i do tego chciałem Indexer. Ponieważ C# nie może wykonywać indeksatorów statycznych, wybrałem podejście Singleton. Ale patrząc na to, podejście to wprowadza zbyt wiele zamieszania (i po prostu wydaje się nie tak), dla zbyt małego zysku. –

Odpowiedz

3

Z pewnością musisz zablokować _list. A ponieważ tworzysz wiele wystąpień na _list nie można zablokować na _list sobie, lecz należy użyć coś takiego:

private static object _listLock = new object(); 

Jak na bok, aby wykonać kilka najlepszych praktyk:

  • DoSomething(), jak pokazano, może być statyczne i tak powinno być.

  • dla klas bibliotecznych zalecany wzorzec jest, aby członkowie statyczne thread-safe, które miałyby zastosowanie do StartRecording(), StopRecording() i DoSomething().

Chciałbym również StopRecording() ustawić _list = null i sprawdzić dla wartości null w DoSomething().

I zanim zapytasz, wszystko to zajmuje tak mało czasu, że naprawdę nie ma powodów, aby wykonać tę czynność. Nie.

+0

Dzięki. Nie martwię się o wydajność (funkcja nagrywania ma funkcję debugowania, która rejestruje każde wezwanie do DoSomething z dodatkowymi danymi), po prostu nie wiem nic o takich rzeczach jak blokada, volatine i monitor. Nie byłem pewien, czego tak naprawdę szukam. Zajrzę do dokumentacji dotyczącej blokady. –

+0

Akceptuję to, ponieważ a) powinien być statyczny, b) blokada rzeczywiście jest poprawna i c) i tak muszę zrestruować. Mam pytanie uzupełniające po moim refactoring: http: // stackoverflow.com/questions/1362995 –

1

Możliwe, choć trudne, napisanie połączonej listy, która umożliwia jednoczesne wstawianie z wielu wątków bez blokady, ale to nie jest to. Po prostu nie jest bezpiecznie wywoływać _list.Add równolegle i mieć nadzieję na najlepsze. W zależności od tego, jak jest napisane, możesz stracić jedną lub obie wartości lub uszkodzić całą strukturę. Po prostu zablokuj.

2

Kolekcje .NET nie są w pełni wątkowe. Od MSDN: "Wielu czytelników może odczytać kolekcję z pewnością, jednak każda modyfikacja kolekcji daje niezdefiniowane wyniki dla wszystkich wątków, które uzyskują dostęp do kolekcji, w tym wątków czytnika." Możesz postępować zgodnie z sugestiami na tej stronie MSDN, aby zapewnić dostęp do wątku bezpieczny.

Jednym z problemów, które prawdopodobnie napotkasz przy obecnym kodzie, jest wywołanie StopRecording, podczas gdy inny wątek znajduje się w środku DoSomething. Ponieważ tworzenie nowej listy z istniejącej wymaga jej wyliczenia, najprawdopodobniej pojawi się stara "Zmodyfikowano kolekcję, operacja wyliczania nie może wykonać".

Najważniejsze: przećwicz bezpieczne nawlekanie!

3

Musisz zablokować listę, jeśli dodaje ją wiele wątków.

Kilka uwag ...

Może jest to powód, by nie, ale chciałbym zaproponować co klasa statyczna i dlatego wszyscy jej członkowie statycznych. Nie ma żadnego prawdziwego powodu, przynajmniej z tego, co pokazałeś, aby wymagać od klientów MyClass wywoływania metody GetInstance() tylko po to, aby mogli wywołać metodę instancji, DoSomething() w tym przypadku.

Nie widzę problemu, który uniemożliwia wielokrotne wywoływanie metody StartRecording(). Możesz rozważyć umieszczenie tam zameldowania, aby w razie nagrania nie tworzyć nowej listy, wyciągając dywan z nóg wszystkich.

Wreszcie, kiedy zamknąć listę, nie rób to tak:

static object _sync = new object(); 
lock(_sync){ 
    _list.Add(new object(somedata)); 
} 

zminimalizować czas spędzony wewnątrz zamka przesuwając nowe stworzenie obiektu poza zamkiem.

static object _sync = new object(); 
object data = new object(somedata); 
lock(_sync){ 
    _list.Add(data); 
} 

EDIT

Mówiłeś, że DoSomething() nie może być statyczna, ale założę się, że można. Nadal możesz używać obiektu MyClass wewnątrz funkcji DoSomething() w celu wykonania jakichkolwiek czynności związanych z instancją. Ale z punktu widzenia użyteczności programowania nie wymagaj od użytkowników, aby MyClass wywoływał najpierw GetInstance(). Rozważ to:

class MyClass { 
    private static MyClass _instance; 
    private static List<string> _list; 
    private static bool IsRecording; 
    public static void StartRecording() 
    { 
     _list = new List<string>(); 
     IsRecording = true; 
    } 
    public static IEnumerable<string> StopRecording() 
    { 
     IsRecording = false; 
     return new List<string>(_list).AsReadOnly(); 
    } 
    private static MyClass GetInstance() // make this private, not public 
    { return _instance; } 
    public static void DoSomething() 
    { 
     // use inst internally to the function to get access to instance variables 
     MyClass inst = GetInstance(); 
    } 
} 

Spowoduje to, użytkownicy MojaKlasa może iść z

MyClass.GetInstance().DoSomething(); 

do

MyClass.DoSomething(); 
+0

Masz rację, powodem, dla którego wykonałem niestatyczny projekt Singleton jest to, że chciałem Indexer (MyClass [SomeThing]), a C# nie obsługuje statycznych indeksatorów. Ale ten projekt dodaje trochę zamieszania, więc zrobię to całkowicie statycznie. –

+0

Gotcha. Pomyślałem, że prawdopodobnie miałeś dobry powód, by robić to w ten sposób. To nie było oczywiste z twojego pytania, to wszystko. –

Powiązane problemy