Komunikacja między wątkami

0

Czołem,

Piszę grę, która będzie wielowątkowa: każdy NPC będzie posiadał swój wątek. Szukam optymalnego sposobu porozumiewania się między nimi. Zakładam, że każdy wątek będzie posiadał swoją kolejkę, na której będzie prowadził nasłuch, a w przypadku otrzymania wiadomości podejmie określone działanie. Zawsze będzie tak, że publikować do kolejki mogą wszyscy, nasłuch i pobieranie wiadomości z kolejki wykonuje tylko jeden wątek.

W Javie istnieje rozwiązanie desygnowane do tego: ArrayBlockingQueue, zastanawiam się, czy coś takiego istnieje również pod C++, czy taką klasę muszę sobie samodzielnie stworzyć?

Ponieważ każdy wątek będzie miał swoją kolejkę, rozsądnym jest obok tablicy wątków ustawienie również tablicy kolejek, przypisanych do tych wątków. Czy jedynym rozwiązaniem jest ustawienie takiej tablicy jako zmiennej globalnej?

I ostatnie pytanie: skoro każdy może publikować do kolejki, a kolejność napływających komunikatów jest obojętna, czy jest sens używać jakichś semaforów, czy też mogę założyć, że dodawanie elementów do kolejki jest procesem bezpiecznym? Nie wiem bowiem jednego:

  1. Kolejka jest zadeklarowana (załóżmy) globalnie. Znajduje się wobec tego w ogólnodostępnej przestrzeni adresowej programu.
  2. Każdy watek (w tym wątek główny programu) ma swoją prywatną stertę na potrzeby wykonywania własnych operacji.
  3. Jeśli wątek_1 wywoła kolejka.add(message1), a wątek_2 chwilę później wywoła kolejka.add(message2), to czy wywołania funkcji add znajdą się na:
  • stercie wątku głównego
  • stertach każdego z wątków, odpowiednio wątku_1 i wątku_2?

Z góry dziękuję za pomoc.

0
Caballito napisał(a)

każdy NPC będzie posiadał swój wątek.
To będzie gra nie tyle wielowątkowa, co wielościnowa... Tzn, szybko ci się zetnie.

0

A czy znasz jakieś inne rozwiązanie, które zapewniłoby mi względną realność - tj. każdy z NPCów będzie miał (względną) swobodę w decydowaniu o sobie?

Myślałem o rozwiązaniu trójwątkowym, czyli mamy wątek bohatera, równolegle wątek świata i wątek NPCów. Wydawało mi się to jednak dające za małą swobodę NPCom.

Niezależnie od tego, komunikacja między watkami pozostaje aktualnym tematem.

0

pula wątków (threadpool) lub kilka pul, komunikacja: dane w tablicach, informacje o zmianach w danych - na zdarzeniach, synchronizacja sekcjami krytycznymi lub muteksami (lub czymś innym z IPC).
ale szczerze mówiąc, jeden wątek by wystarczył na wszystko.

0
ŁF napisał(a)

ale szczerze mówiąc, jeden wątek by wystarczył na wszystko.
Aj, jednym zdaniem zburzyłeś całą moją koncepcję :) I teraz zamiast kombinować nad rozwiązaniem, które już miałem w głowie, szukam nowego...

Hmm... faktycznie wygląda to rozsądnie: program biega po świecie w nieskończonej pętli , przegląda na bieżąco tablice lokacji i NPCów (biega dopóki nie okaże się, że wszystkie kolejki sa puste - wtedy przysypia na kilkadziesiąt milisekund, żeby nie generować pustych obiegów), w międzyczasie śledzi standardowe wejście (można tak zrobić, żeby ruch na stdin budził wątek z uśpienia?) i reaguje na komendy.

Dzięki!

0

Temat mnie dość zainteresował, bo też mam robię grę (MMORPG), ale jako że nie jestem superdoświadczonym programistą, to wolę się nie wystawiać na pośmiewisko...

Co do ruchu na klawiaturze - owszem, można. Tylko trzeba zrobić mniejszą pętlę. W pseudokodzie będzie to tak (najprostsze rozwiązanie):

int uspij_na = XX;
int czas_poczatek = pobierz_czas();
while((!nacisnieto_klawisz()) && (pobierz_czas() < czas_poczatek+uspij_na)){}

Być może w tej pętli dałem niepotrzebnmie za dużo nawiasów, ale nie zaszkodzą one, a mogą pomóc (kolejności operatorów NIE mam wykutej na blachę...).

Funkcja wcisnieto_klawisz() to może być kbhit() chyba z conio.h, nie pamiętam.

I ja też mam problem z wątkami - chcę, aby w serwerze główny wątek odbierał informacje z socketów, i wstawiał je do kolei, potem wątek 2 będzie to przetwarzał, i wszadzał do innej kolejki, z której wątek 1 będzie czytał i wysyłał do socketów. Ujdzie takie coś?

0

@Caballito: WaitForInputIdle

@olo16: z nawiasów w warunku, który podałeś, są potrzebne tylko dwa, do tego taka pętla całkowicie obciąży jeden rdzeń - musisz dodać tam sleep'a, zapomniałeś też o myszy. co do Twojego pytania - ujdzie, wszystko jest kwestią tego, jak to oprogramujesz. ja bym sugerował inne rozwiązanie, skoro tcp oferuje dla każdego połączenia osobny wątek; w takim przypadku nie zachodzi potrzeba synchronizacji, a to powoduje, że jest to najprostsze rozwiązanie. inna sprawa jeśli równoczesnych połączeń jest kilka tysięcy i więcej, wtedy można to zrobić tak jak chcesz. do komunikacji z wątkiem roboczym dwa semafory, do komunikacji z wątkiem odsyłającym dane eventy lub komunikaty.

// tagi się rozjechały - deus

0

Kurde, wychodzi na to że jestem strasznym noobem... Skoro TCP daje możliwość użycia nowego wątku do każdego połączenia, to jak to zrobić na socketach w BCB? Czy to kwestia ustawienia socket type (thread_blocking lub nonblocking)?

Co do ilości połączeń to nie wiem, ile ich będzie, raczej mało, ale chyba lepiej je obsługiwać w jednym wątku... No i na pewno potrzebny mi będzie wątek pomocniczy - wątek główny BCB jest eventowy, a mój roboczy to będzie pętla.

Następna rzecz - co to tak na chłopski rozum jest ten semafor? Trochę o tym czytałem ale niewiele rozumiem. Czy to będzie mi potrzebne - w końcu nie będzie wspólnych sekcji kodu - tylko wątek główny z socketami będzie dodawał wiadomości do kolejki, a wątek roboczy będzie je odczytywał i usuwał.

Kolejna rzecz - komunikacja roboczy -> odsyłający: nie wiem czy mówiłeś o eventach w rozumieniu winapi czy w rozumieniu VCL, ale dla mnie byłby dobre te VCLowskie - wątek rooczy dokłada wiadomość do kolejki wysyłania, odpala event w wątku głównym, a ten wysyła co ma wysłać.

0

Z opisu w pierwszym poście wynika, że chodzi Ci o zrobienie czegoś takiego:
http://theron.ashtonmason.net/

0

Przebudowuje posta, bo znalazlem kilka odpowiedzi:

@LF
Znalazalem, ze WaitForInputIdle sprawdzi sie w przypadku GUI, ale nie konsoli.

Szukalem rowniez Windowsowego odpowiednika select(), ale niestety - windowsowy select() dziala tylko dla gniazd, a strumien wejsciowy gniazdem nie jest.

Znalazlem kolejna rzecz: asynchroniczne wejscie/wyjscie. Zrobilem cos takiego i mam jeden problem: program powinien czekac az wprowadze cala wiadomosc, potwierdze enterem, wtedy dopiero ma ja obsluzyc (w przypadku ponizszego kodu: tylko wyswietlic). To jednak, co napisalem, zatrzymuje caly proces po pojedynczym wcisnieciu klawisza i czeka na moje wejscie. Czy ktos moze mi podpowiedziec, co powinienem zmienic?

#include <windows.h>
#include <winbase.h>
#include <string>
#include <iostream>
using namespace std;
int main(){

struct _OVERLAPPED over;

ULONG_PTR internal = 0;
ULONG_PTR internalHigh = 0;
PWORD pointer = 0;
over.Internal = internal;
over.InternalHigh = internalHigh;
over.Offset = 0;
over.OffsetHigh = 2000;


struct _SECURITY_ATTRIBUTES secat;
secat.nLength = sizeof (struct _SECURITY_ATTRIBUTES);
    secat.lpSecurityDescriptor = NULL;
    secat.bInheritHandle = FALSE;


    HANDLE conin = CreateFile("CONIN$",//console input
    GENERIC_READ,
    FILE_SHARE_READ,
    &secat,
    OPEN_EXISTING,
    0,
    0);


char bufor[2000];
DWORD i;

HANDLE handle[1];
handle[0]=conin;

while (1){
cout <<"I start my loop...";
DWORD test = WaitForMultipleObjects(  1, handle,  TRUE,  5000);

if (test ==WAIT_TIMEOUT) cout <<" ...Timeout!"<<endl;
else if (test ==WAIT_FAILED) cout <<"error!"<<endl;

else {
for (int ii = 0; ii<2000; ii++) bufor [ii] = '\0';
ReadFile (conin, bufor, 2000, &i, &over);
cout << "I got: " << bufor;
}

}

Z góry wielkie dzięki za każdą pomoc!

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