2012-01-02 13 views
11

Masz dwa wątki, aib. Wątek a znajduje się w pętli na zawsze, nasłuchując na gnieździe blokującym 1. Gwint b również znajduje się w pętli na zawsze, nasłuchując na gnieździe blokującym 2. Zarówno gniazdo 1, jak i gniazdo 2 mogą zwracać dane w dowolnych momentach, więc wątek a może spać na zawsze oczekiwanie na dane, natomiast wątek b nieustannie pobiera dane z gniazda i kontynuuje jego przetwarzanie. To jest tło.Scenariusz komunikacji w wątku Haskell

Załóżmy teraz, że muszą udostępnić słownik. Kiedy wątek a pobiera niektóre dane (jeśli w ogóle) dodaje parę kluczy do słownika po pewnym przetworzeniu, a następnie nadal czeka na więcej danych. Kiedy wątek b odbiera dane z gniazda, najpierw sprawdza słownik, aby sprawdzić, czy istnieją informacje związane z danymi, które otrzymał, zanim przejdzie do jego przetwarzania. Nie ma żadnych skreśleń do słownika, tylko wstawki i zapytania (byłbym zainteresowany, gdyby to miało wpływ na ostateczne rozwiązanie).

W standardowym języku imperatywnym, takim jak python lub c, jest to dość łatwe do zrobienia poprzez udostępnienie słownika w obu zakresach i zapytanie tylko po tym, jak wątek uzyskał blokadę, więc wątek B zawsze widzi najbardziej (dobrze prawie) aktualny słownik.

W Haskell, wydaje mi się, że staram się wymyślić dobrą implementację tego wzorca. MVars, może mieć tylko jeden element na raz, więc nie może być tym, że Thread a umieszcza w słowniku, ponieważ może pojawić się nowa aktualizacja i nie będzie w stanie przesłać tego nowego słownika, dopóki wątek b nie pobierze go z MVar. Z drugiej strony, jeśli wątek b używa MVar do wysłania gotowego sygnału "ok!" do wątku a, może się zdarzyć, że wątek a śpi na swoim czytanym gnieździe, więc nie będzie w stanie odesłać danych, dopóki jego gniazdo odczytu nie zostanie odblokowane! Są też kanały, ale wydaje się to nieporządne, ponieważ będę musiał wysyłać nowe słowniki, a wątek B odrzuci wszystkie oprócz ostatniego.

Alternatywnym rozwiązaniem, które mogłoby działać, jest po prostu wysłanie aktualizacji w dół kanału, a wątek B sam skonstruuje słownik. Jednak zastanawiam się, czy istnieją lepsze alternatywne rozwiązania.

Dziękujemy za poświęcenie czasu na przeczytanie tego bardzo długiego pytania!

+4

Nie widzę problemu z 'MVar's. Jeśli A zostanie przebudzone i otrzyma nowe dane, spróbuje 'wziąćMVar' słownik z' MVar'.Gdy się powiedzie, zaktualizuj słownik, słownik 'putMVar', z powrotem do trybu uśpienia. Gdy B otrzyma dane, spróbuj 'takeMVar', sprawdź ponownie. Gdzie to się rozkłada? –

+1

Dzięki! Strzelaj Przepraszam, to dokładnie to, czego szukałem, nieważne. Wciąż jestem początkującym. Myślałem, że nie może tego znieść, jeśli to włoży, i tylko inna nitka może. Głupie założenie z tyłu mojej głowy! Po prostu nie wiem, jak teraz zamknąć to pytanie? –

+0

Mogę sprawić, że będzie to odpowiedź, którą możesz zaakceptować, aby oznaczyć sprawę jako rozwiązaną, lub możesz usunąć pytanie, jeśli wolisz (nie wiem jak, gdzieś powinien znajdować się link do usunięcia). –

Odpowiedz

10

Można użyć MVar w następujący sposób:

  • Kiedy nawlec pobiera nowe dane, próbuje dostać się do słownika z takeMVar. Gdy to się powiedzie, aktualizuje słownik i umieszcza go ponownie w MVar
  • Kiedy wątek B otrzymuje dane, próbuje uzyskać słownik z takeMVar - w powyższym scenariuszu, w którym rzadko dane są odbierane średnio szybko. Następnie wykonuje wyszukiwanie i przywraca słownik.

hammar Jak podkreślił, to chyba lepiej nie korzystać bezpośrednio takeMVar i putMVar ale raczej zawinąć je w modifyMVar_ wzgl. modifyMVar, aby nie pozostawić pustego MVar, jeśli jeden wątek otrzymuje wyjątek podczas korzystania ze słownika.

W wątku A, coś

modifyMVar_ mvar (\dict -> putMVar mvar (insert newStuff dict)) 

w wątku B Wszystko czego potrzebujesz to prosty readMVar (dzięki ponownie @hammar za wskazanie, że obecnie).

+0

Dla wątku B, myślę, że 'readMVar' będzie wystarczający (i być może będzie bardziej wydajny), ponieważ słownik nie jest aktualizowany. – hammar

+0

Prawdopodobnie. Jest bardzo mało prawdopodobne, że zostanie trafiony przez wyjątek między ujęciem a wprowadzeniem, ale nie jest niemożliwy. Wtedy 'MVar' może zakończyć się pusty. Z drugiej strony jest całkiem możliwe, że wyjątek musiałby i tak zabić cały proces. –

+0

Implementacja 'readMVar' używa' maski_', więc nie sądzę, aby wyjątek mógł się zdarzyć między wzięciem a wstawieniem. – hammar

Powiązane problemy