Różnice między wybraną wersją a wersją aktualną.
Last revision Both sides next revision | |||
aktywny_obiekt [2008/04/15 20:23] krogala utworzono |
aktywny_obiekt [2008/04/15 20:41] krogala |
||
---|---|---|---|
Linia 1: | Linia 1: | ||
- | ===Wzorzec Projektowy Aktywny Obiekt=== | + | ====== Wzorzec Projektowy Aktywny Obiekt ====== |
autor: Krzysztof Rogala | autor: Krzysztof Rogala | ||
+ | ==== Problem ==== | ||
Wyobraźmy sobie bazę danych (już istniejącą). Jedni użytkownicy mogą czytać z niej dane, a innni je zmieniać. Ci pierwsi (dalej: czytelnicy) są niezadowoleni, ponieważ czasem z bazy odczytują nieaktualne lub bezsensowne dane. Ci drudzy (dalej: pisarze) także są niezadowoleni, ponieważ podczas oczekiwania na wprowadzenie poprawek do bazy, nic nie mogą robić. Rozwiązaniem tego problemu jest synchronizacja dostępu. Nie może być tak, że jednocześnie ktoś pisze do bazy i z niej czyta. Preferowani w takiej sytuacji będą pisarze. Ponadto założyłem | Wyobraźmy sobie bazę danych (już istniejącą). Jedni użytkownicy mogą czytać z niej dane, a innni je zmieniać. Ci pierwsi (dalej: czytelnicy) są niezadowoleni, ponieważ czasem z bazy odczytują nieaktualne lub bezsensowne dane. Ci drudzy (dalej: pisarze) także są niezadowoleni, ponieważ podczas oczekiwania na wprowadzenie poprawek do bazy, nic nie mogą robić. Rozwiązaniem tego problemu jest synchronizacja dostępu. Nie może być tak, że jednocześnie ktoś pisze do bazy i z niej czyta. Preferowani w takiej sytuacji będą pisarze. Ponadto założyłem | ||
także, że na raz tylko jeden czytelnik korzysta z bazy. Co więcej, wprowadzamy rozdzielenie wywołania metody i jej wykonania, co pozwala pisarzom natychmiast przejść do innych zadań. | także, że na raz tylko jeden czytelnik korzysta z bazy. Co więcej, wprowadzamy rozdzielenie wywołania metody i jej wykonania, co pozwala pisarzom natychmiast przejść do innych zadań. | ||
Linia 19: | Linia 20: | ||
boost::mutex io_mutex; | boost::mutex io_mutex; | ||
</code> | </code> | ||
+ | ---- | ||
+ | ==== Budowa "aktywnego obiektu" ==== | ||
Wzorzec "aktywny obiekt" wykorzystuje następujące klasy: | Wzorzec "aktywny obiekt" wykorzystuje następujące klasy: | ||
**Proxy** - klasa widoczna dla programisty, która zawiera interfejs obiektu | **Proxy** - klasa widoczna dla programisty, która zawiera interfejs obiektu | ||
Linia 46: | Linia 48: | ||
class Reader; | class Reader; | ||
</code> | </code> | ||
- | Najpierw implementujemy bazę danych i metody, które będą na niej wywoływane. | + | ===== Implementacja klas ===== |
+ | ==== Baza danych ==== | ||
+ | Klasa reprezentująca faktyczną bazę danych, oraz implementująca metody na niej wywoływane | ||
<code cpp> | <code cpp> | ||
class Database { | class Database { | ||
Linia 66: | Linia 70: | ||
}; | }; | ||
</code> | </code> | ||
- | Teraz implementujemy klasę opakowującą wartości zwracane | + | ==== Wartości zwracane ==== |
+ | Klasa ta opakowuje wartość //int//, zwracaną przez funkcję //read//. Umożliwia ona automatyczną lecz blkującą konwersję na //int//, lub na pobranie wartości za pomocą funkcji, jeżeli ta jest już gotowa. | ||
<code cpp> | <code cpp> | ||
class RetVal { | class RetVal { | ||
Linia 107: | Linia 112: | ||
}; | }; | ||
</code> | </code> | ||
- | Następnie implementujemy żądanie wywołania metody. Zawiera ono funkcję **guard** służącą do zapewnienia spełnienia dodatkowych warunków synchronizacji, oraz **call**, wywołującą właściwą metodę | + | ==== Żądanie ==== |
+ | Implementujemy żądanie wywołania metody. Zawiera ono funkcję //guard// służącą do zapewnienia spełnienia dodatkowych warunków synchronizacji, oraz //call//, wywołującą właściwą metodę | ||
<code cpp> | <code cpp> | ||
class Request { | class Request { | ||
Linia 153: | Linia 159: | ||
}; | }; | ||
</code> | </code> | ||
- | Implementujemy planistę. | + | ==== Planista ==== |
+ | Planista odpowiadający za uszeregowanie metod, korzysta z wewnętrznej kolejki std::queue | ||
<code cpp> | <code cpp> | ||
class Scheduler { | class Scheduler { | ||
Linia 217: | Linia 224: | ||
}; | }; | ||
</code> | </code> | ||
- | I na koniec implementujemy proxy | + | ==== Proxy ==== |
+ | Klasa będąca interfejsem dla programisty. Tworzy ona żądania wywołania do metod, a do wątku wywołującego metodę natychmiast zwraca wartość. | ||
<code cpp> | <code cpp> | ||
class DBProxy { | class DBProxy { | ||
Linia 244: | Linia 252: | ||
DBProxy base; | DBProxy base; | ||
</code> | </code> | ||
- | Implementuję pisarza i czytelnika | + | ===== Przykład użycia ===== |
+ | |||
+ | ==== Pisarz i czytelnik ==== | ||
+ | To prosta implementacja pisarza i czytelnika. Jedyne co robią to wpisują/czytają dane z bazy i spią. Można jednak wyobrazić sobie jak np pisarz wymyśla dane (np wyszukuje), a czytelnik jakoś odczytane dane przetwarza. Każdy będzie wywoływał się w osobnym wątku. Do podziału na wątki wykorzystałem boost::thread. Warto też zwrócić uwagę na utworzenie z wypisywania sekcji krytycznej i synchronizacji przy pomocy mutexa. | ||
<code cpp> | <code cpp> | ||
class Writer { | class Writer { |