Pobieranie wartości pól obiektu, w trakcie ich aktualizacji.

0

Witam,
Moja sytuacja wygląda następująco. Mam klasę ze zmiennymi string, powiedzmy

public class  Zamowienie
{
public string numerZamowienia;
public string czas;
public string rodzaj;
public string ilosc;
...
//konstruktor
public Zamowienie(string klucz)
{
this.numerZamowienia=klucz;
}
}

Obiekty tej klasy tworzę i zapamiętuję w kolekcji Dictionary. Kluczem jest numerZamowienia, a wartością Zamowienie.
Mój program ciągle, w osobnym wątku, "pracuje" z tymi obiektami.
I tu pojawia się moje pytanie. Co się stanie jeżeli w trakcie aktualizacji danych w obiekcie zechce pobrać wszystkie wartości pól tego obiektu?

Czyli coś takiego:

  1. przywołaj obiekt klasy Zamówienie z numerZamowienia=6
  2. aktualizuj pole czas
    <--- w tym momencie mój drugi wątek zapytał o ten sam obiekt, ale odczytuje wartości z numeruZamówienia=6, czyli czas (już zmieniony)
    Zapisywanie nowych wartości stoi w miejscu, myśli co by tu wpisać w pozostałe pola, a wątek odczytujący czyta dalej - string rodzaj, string ilość.
  3. Dopiero teraz metoda aktualizująca wartości pól obiektu aktualizuje pozostałe pola.

W wyniku czego wątek odczytujący zwraca nam dla numeru zamówienia 6:
nowy czas
poprzedni rodzaj
poprzednią ilość

Czy takie "pomieszanie" wartości pól obiektu (aktualne, poprzednie) jest możliwe? A może dzieje się tak, że wykrywany jest dostęp do obiektu, a wtedy odczytywanie wartości pól z tego obiektu wykonywane jest na kopi poprzedniego stanu?
Jeżeli opcja 1 w moim przypadku jest możliwa, to jak tego uniknąć?

Dzięki za pomoc.

4

Jesteś na etapie, ze warto abyć poczytał co to jest obiekt niezniemmiczy, bardziej znany z angielska immutable
Użyjesz albo nie, ale z takiej "ewangelizacji" poszerzysz horyzonty.

Tak, rzeczywiście zmienność pól w obiekcie może stworzyć (duże) problemy
Nie, nie ma automagicznych zabezpieczeń.

0

@AnyKtokolwiek: To jeszcze pytanie mam. Czy istnieją inne sposoby niż zalecony? Możesz coś pokrótce zaproponować?

2
duzers napisał(a):

@AnyKtokolwiek: To jeszcze pytanie mam. Czy istnieją inne sposoby niż zalecony? Możesz coś pokrótce zaproponować?

Nie przychodzi mi nic innego, niż wzorce immutable, od bardzo scisłych ("uniwersyteckich") po "realne" (ale nie gwałcące istoty).

I zawsze mi myśl wraca do metod aktualizujących obiekt. Pomyśl, że nawet "tradycyjne" są już jakościowo inne od setterów (propertisów C#)

O ile "tradycyjnie" by były void
void ZmieńDatęZamówienia(nowa data)
to w modelu immutable zwracają nowy egzemplarz obiektu po zmianach *)
Zamowienie ZmieńDatęZamówienia(nowa data)

Metody aktualizujące obiekt, pomijając całe zamieszanie wątkowe, mają (w porównaniu do seterów) zalety biznesowe:

  • operacja jest nazwana
  • jest (mniej czy bardziej) transakcyjna (sensowny zestaw operacji albo wcale: np data i autor zmiany)
  • da się określić security czy coś podobnego

*) oczywiście wymaga to zmiany paradygmatu i przekonstruowania reszty kodu.
Zwróć uwagę, że w Twoim przykładzie, te drugi wątek dostanie stary obiekt, albo nowy, ale spójny

0

@AnyKtokolwiek:
Co to znaczy, że obiekt jest immutable?
Niezmienialny obiekt to taki, który raz zainicjalizowany nie zmieni swojego stanu. O co dokładnie chodzi? Mamy gwarancję, że wszystkie dane (pola) w konkretnym obiekcie nie ulegną zmianie. Żeby zmodyfikować jakieś wartości, trzeba utworzyć całkowicie nowy obiekt.

Już widzę, że kod będzie do zmiany, ponieważ klasa z Dictionary obsługująca ten obiekt, zwracała obiekt, jak już był wcześniej utworzony, lub tworzyła nowy, gdy go nie było. Ponadto będzie problem z bieżącym odczytem obiektu, w trakcie zmian, bo obiekt wtedy może w każdej chwili zniknąć.
Nie wiem czy nie lepszym pomysłem nie byłoby zrobienie jakiegoś sygnalizatora, żeby w trakcie zmiany wartości pól obiektu, metoda odczytująca poczekała na pełną zmianę.

0

W praktyce nie ma seterów (w Javie), propertisy nie mają publicznego set (C#), pola nie "wystają" gołe (chyba że final / readonly - to jest ta część, którą ja "wybaczam" w realnym programowaniu).

duzers napisał(a):

Ponadto będzie problem z bieżącym odczytem obiektu, w trakcie zmian, bo obiekt wtedy może w każdej chwili zniknąć.

W systemach z GC (Java/C#) obiekt tzw "stary" nie zniknie, dopóki ktoś ma do niego referencję. Moze być mniej/nie aktualny.
Więc to głęboko zależy, do czego taki odczyt.
(Bohdan Smoleń: )

Nie wiem czy nie lepszym pomysłem nie byłoby zrobienie jakiegoś sygnalizatora, żeby w trakcie zmiany wartości pól obiektu, metoda odczytująca poczekała na pełną zmianę.

Mówisz o synchronizacji, to też niebanalna sztuka, i znów się oprze o nazwane metody zmieniające, a nie setery

0

@AnyKtokolwiek:

Mówisz o synchronizacji, to też niebanalna sztuka, i znów się oprze o nazwane metody zmieniające, a nie setery

No mógłbym zrobić jakąś zmienna, której stan powie czy metoda pobierająca dane ma używać kopii obiektu, czy już starego obiektu po zmianach.
Może Lock na obiekcie zrobić?

0
duzers napisał(a):

@AnyKtokolwiek:

Mówisz o synchronizacji, to też niebanalna sztuka, i znów się oprze o nazwane metody zmieniające, a nie setery

No mógłbym zrobić jakąś zmienna, której stan powie czy metoda pobierająca dane ma używać kopii obiektu, czy już starego obiektu po zmianach.
Może Lock na obiekcie zrobić?

Strasznie przekombinowana wizja

Poczekajmy na inne wypowiedzi. Jak nie umiem podać tego z wyważoną perspektywą (w skrócie: rzeczy do synchronizacji maja złą prasę)

4

Immutability rozwiązuje problemy z niechcianymi zmianami stanu, a nie z równoległym dostępem. Wciąż dwa wątki mogą wziąć taki obiekt "immutable", zmienić go, a potem wstawić do słownika i ostatni wątek wygra - pytanie, czy słusznie?

duzers napisał(a):

Może Lock na obiekcie zrobić?

Albo użyć ConcurrentDictionary.

0

@somekind:
W moim przypadku próba odczytywania danych wykonywana jest co 100 ms, a sam proces aktualizacji danych z sieci (w osobnym wątku) trwa trochę ponad 1 sekundę, więc na razie chyba wystarczy indicator, informujący o końcu zmiany, a do tego czasu każda próba odczytywania danych wykonywana jest na kopii Dictionary.
Do tego wszystkiego jednocześnie o te dane może zapytać kilku klientów jednocześnie, więc Lock() chyba nie jest dobrym pomysłem (opóźni każdego klienta?)

ConcurrentDictionary - poczytam.

1

Wątek równoległy może przesłać zmiany w evencie. A obsługiwać go będzie wątek główny. Pozbywamy się wtedy problemu zapisu i odczytu w jednym momencie

0

Poczytaj o eventach, następnie przekaż handler do nowego wątku i używaj go, wybacz ale komputera nie mam przy sobie a z komórki to samobójstwo pisać kod 😁

2

Z ciekawości. Jak ma sens ma u Ciebie odczytywanie co 100 ms skoro aktualizacją trwa 1 s.
Jak pisal @somekind, ConcurrentDictionary chyba rozwiązuje problem.
Ja bym xhyba zrobił event po aktualizacji i nie potrzebujesz żadnego sprawdznia co 100 ms.

1

No właśnie - skoro czytasz częściej niż piszesz, to brzmi jakbyś dane chciał mieć aktualne. A w tej sytuacji, to może lepiej odwrócić przepływ i niech źródło danych informuje o jego aktualizacji klientów? Może w ogóle żaden słownik nie jest potrzebny.

0
jacek.placek napisał(a):

Z ciekawości. Jak ma sens ma u Ciebie odczytywanie co 100 ms skoro aktualizacją trwa 1 s.

To wymusza program nadrzędny (ze swoim językiem programowania), a mój program jest pluginem do tego programu, z funkcjami czytającymi poszczególne dane z kolekcji klas zawierające te dane. Całość nadzoruje FORMa (jeden z elementów mojego plugina), która uruchamia się po zalogowaniu. Póki nie ma nowych danych, to funkcja czyta ciągle poszczególne (użytkownik decyduje w funkcji jakie) składowe tej kolekcji (co 100 ms). Plugin ma także, można powiedzieć Event, który informuje o przybyciu nowych danych. W tym momencie FORMa pobiera te dane/wprowadza zmiany do już istniejących w kolekcji i przy którymś zapytaniu funkcja w programie nadrzędnym odbiera już nowe dane.
Na razie zrobiłem tak, że po Evencie ustawiana jest zmienna, która nakazuje funkcji pobierać dane z kopii kolekcji, a jak już Event skończy aktualizować dane w kolekcji, to umożliwiam pobieranie już z oryginalnej kolekcji (aktualizacja trwa właśnie około 1 s).

1 użytkowników online, w tym zalogowanych: 0, gości: 1