wielowatkowe sprzatanie

0

Witam,
Zastanawiam sie jak najefektywniej(najprosciej) usunac obiekty bazowe tworzonych watkow i przechowywanych w kolekcji.

Watki sa odłączone (bez join()).
Watki tworze z uzyciem boost::thread() i przekazuje do nich referencje do obiektow bazowych z kolekcji.
W momencie kiedy te watki sie koncza, konczy sie watek.

Ale nie sa wywolywane destruktory tych obiektow bazowych.
Jak usune this na wyjsciu z watku, to zostanie zly wskaznik w kolekcji i nie bede wiedzial ktorego elementu z tej kolekcji sie pozbyc.

Mam kilka pomyslow ale nie sa proste.

0

Prosta zasada - sprzątaj tam gdzie tworzysz.
Poza tym możesz zrobić tak jak do tej pory opisałeś tylko w momencie usuwania obiektu usuwasz go również z kolekcji.

0

Zasade znam,
problem w tym ze obiekt w wątku nie zna obiektu ojca/matki ktory go tworzy.
Wiec chcialbym usunac go z kolekcji w obiekcie ojca/matki ale:

  1. Nie wiem kiedy watek sie zkonczy,
  2. Nie wiem jaki watek sie skonczyl,

Przyklad klient-server:

class acceptor
{
        public: 
            accept()
             {
                for(;;)
                {
                    _userSock = new boost::...::socket(io);
                    _main.accept(_userSock); // blokujaca
                    _c.push_back(new client(_userSock));
                    boost::thread t(boost::ref(*c.at(c.size()-1)));
              };
   
        private:
           collection<client*> _c;
           socket _main;
           socket* _userSock;

}
class client
{
    public:
        client(sock*s):socket(s)
        { 
             buf = new Buffor();
             async_read(socket);
         };
        ~client()
        { 
             delete socket;
             delete buf;
         };
        void operator()()
        {
              socket->get_service().run(); // blokuje watek dopuki async_read sie nie wykona
                // wychodzac z tej funkcji konczy watek
         }
    private:
        sock* socket;
        Buffor *buf;
}

Moglbym zrobic w ten sposob, ze watek przyjmowalby funkcje lokalna do "zdetachowania",
i tam by byl tworzony obiekt lokalny i wraz z koncem jego zasiegu zwolnione zasoby. Tyle ze to wymaga duzej restrukturyzacji programu.

0

Ale po co ci wątek jak odbierasz asynchronicznie ?
Niech jeden wątek akceptuje połączenia i tworzy klientów, a drugi niech "czatuje" na zakończenie pobierania przez któregoś z klientów. Nie znam boost więc dokładnie ci nie powiem jakie metody, znajdziesz sobie na pewno.
Bo z tego co na razie pokazałeś to asyn_read nie ma sensu bo i tak blokujesz wątek, dopóki read się się nie zakończy.

Jakbyś to chciał zrobić na wątkach to wywołaj zwykły read synchroniczny.
No i w boost na pewno jest wiele mechanizmów aby poczekać na jeden z uruchomionych wątków. Może jest coś takiego jak join_any_thread, a jak nie to użyj zdarzeń - wątek jak kończy prace sygnalizuje zdarzenie.

Generalnie chodzi o to aby były dwa "główne" wątki - jeden akceptuje, drugi komunikuje się z wątkami/czak na ich zakończenie.

0

To tylko przyklad, logika klasy "client" jest duzo bardziej skomplikowana i tego wymaga.
poza tym aplikacja bedzie pracowac na maszynach wielordzeniowych.

0

No to tak jak pisałem. Tylko skoro wątek akcesora jest cały czas zblokowany to bez sensu żeby trzymał kolekcję klientów w prywatnym polu jak i tak nie będzie miał okazji z niej korzystać. Generalnych schemat mógłby być taki:

#wątek główny
kolekcja_klientów;

obsługuj_gui_i_komunikaty_od_wątków_lub_akceptora()
{
   switch(komunikat)
   case zaakceptowano_klienta: 
      dodaj_utworzony_przez_akceptor_obiekt klienta_do_kolekcji();
   case zakonczono_watek:
      usun_klienta_z_kolekcji();
   case komunikacja_z_watkiem_klienta_na_jakis_inny_temat: 
   ...
  
}

#akceptor
while(!koniec)
{
   polaczenie = akceptuj;
   klient = utworz(polaczenie);
   powiadom_watek_glowny(zaakceptowano_klienta, klient);
}

#klient
while(!koniec)
{
   komunikacja_przez_socket();
   komunikcja_z_watkiem_glownym(); //np. powiadom o postępie etc.
}
powiadom_watek_glowny(zakonczono_watek, klient);

Pisząc "komunikat" mam na myśli dowolną formę komunikacji (komunikaty windows, zdarzenia, APC ...).
W czym masz GUI ? Jaki OS ? Ważne żeby wpleść tą komunikację wątku głównego w obsługę GUI.

0

Brak GUI, aplikacja serverowa.
Z tego co do tej pory znalazlem to jest metoda boost::on_thread_terminate() lub cos podobnego (pisze z pamieci), wiec tam bym mogl wyslac komunikat.
Pytanie czy ta metoda ma ta referencje ktora podalem na poczatku.

Sygnaly to jedno z rozwiazan ktore rozwazalem. Ale tak jak pytalem chcialem to zrobic jak najprosciej.
Taki sygnal musialby wysylac klient jeszcze na wyjsciu, pytanie tylko czy mozna by go bezpiecznie usunac kiedy on jeszcze zyje przez moment.

Jesli chodzi o joinowanie, to na pewno nie moglbym go zrobic w klasie akceptora, bo on musi tworzyc caly czas nowych klientow. (ewentualnie w watku rownoleglym ktory obsluguje sygnaly),

Sprawdze jeszcze raz, ale nie znalazlem "join_any() like" metody na razie. A jesli istnieje to musialaby zwracac wskanik na watek ktory sie skonczyl, wtedy wiedzialbym ktory obiekt klienta sie skonczyl.

Async_read dziala prawie jak select() na unixie wiec to nie async_read jest blokowany tylko watek, w miedzy czasie mozna zrobic cos innego np. odebrac cos na innym porcie. Dlatego synchroniczny read nie wchodzi w gre.

0

Taki sygnal musialby wysylac klient jeszcze na wyjsciu, pytanie tylko czy mozna by go bezpiecznie usunac kiedy on jeszcze zyje przez moment.
Mogą wystąpić komplikacje. Np. jeśli lokalny obiekt utworzony w operator() podczas niszczenia korzysta z pól klasy klient. Ewentualne pułapki można prosto wykluczyć - kiedy wątek główny otrzyma sygnał zakończenia od klienta to zanim go zniszczy niech wywoła join na jego wątku.

Powiedz mi jeszcze, czy ty w ogóle korzystasz z tej kolekcji klientów ? Czy tylko dodajesz/usuwasz do niej klientów i tyle ?

Kwestie komunikowania wątku głównego możesz zrealizować za pomocą kolejki komunikatów tej oferowanej przez boost.
Natomiast w drugą stronę tj. odpowiedź na komunikat od wątku głównego do klienta (jeśli w ogóle to potrzebujesz) możesz zrealizować inną formą, zwykłe zdarzenia oferowane przez boost (condition variable) powinny wystarczyć, czy tam jakiś muteks/sekcja krytyczna, zależy jak ta komunikacja ma przebiegać.

msg
{
   typ;
   klient;
   //+ eventualne inne dane
}

// akceptor //////////////////////////////////////////////////////////
while(!fajrant)
{
   polaczenie = akceptuj();
   klient = utworz(polaczenie);
   glowny.kolejka.send(msg(zaakceptowano, klient));
}

// klient //////////////////////////////////////////////////////////
while(!klient_fajrant)
{
   //...
   //komunikacja dwustronna
   glowny.kolejka.send(msg(costam, this));
   klient.condition_variable.wait();
   rob_cos(klient.odpowiedź);
   //...
}
//koniec pracy
glowny.kolejka.send(msg(koniec, this));

// główny //////////////////////////////////////////////////////////
while(!fajrant)
{
   msg = glowny.kolejka.get();
   switch(msg.type)
   {
      case zaakceptowano:
         kolekcja.dodaj(msg.klient);
 
      case costam:
         msg.klient.odpowiedź = rob_costam();
         msg.klient.condition_variable.notify();

      case koniec:
         msg.klient.watek.join();
         delete msg.klient;
         kolekcja.usun(msg.klient);
      //...
   }
}

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