2010-07-26 14 views
6

W moim programie jest jeden wątek (odbierający wątek), który jest odpowiedzialny za odbieranie żądań z gniazda TCP i istnieje wiele wątków (wątków roboczych), które są odpowiedzialne za przetwarzanie odebranych upraszanie. Po przetworzeniu żądania muszę wysłać odpowiedź przez TCP.Jak anulować oczekiwanie w select() na Windows

Tutaj jest pytanie. Chciałbym wysłać dane TCP w tym samym wątku, którego używam do odbierania danych. Ten wątek po otrzymaniu danych zwykle czeka na nowe dane w select(). Tak więc, gdy wątek roboczy zakończy przetwarzanie żądania i umieści odpowiedź w kolejce wyjściowej, musi zasygnalizować wątkowi odbierającemu, że istnieją dane do wysłania. Problem polega na tym, że nie wiem, jak anulować czekanie w select(), aby wyjść z oczekiwania i zadzwonić pod numer send().

Czy mogę użyć innego wątku wyłącznie do wysyłania danych przez TCP?

Updated

MSalters, Artem dziękuję za was odpowiedzi!

MSaltery, po przeczytaniu odpowiedzi znalazłem tę stronę: Winsock 2 I/O Methods i przeczytałem o WSAWaitForMultipleEvents(). Mój program w rzeczywistości musi działać zarówno na HP-UX, jak i na Windowsie. W końcu zdecydowałem się na podejście zaproponowane przez Artema.

+0

Dlaczego używasz 'select()' do pisania? Zazwyczaj 'select()' tylko czeka na odebrane dane, i możesz 'wysłać()' danych za pomocą tych samych gniazd w międzyczasie. – ereOn

+0

Nie, nie używam opcji wyboru do zapisu. Czy ja to powiedziałem? –

+0

@skwllsp: Założę się, ponieważ wydaje się, że chcesz anulować 'select()', aby napisać do twojego gniazda, co nie jest konieczne. Ale może źle zrozumiałem całość, mój angielski nie jest dokładnie rodzimy. – ereOn

Odpowiedz

13

Musisz użyć czegoś podobnego do triku bezpiecznego, ale w twoim przypadku musisz użyć pary połączonych gniazd TCP.

  1. Utwórz parę gniazd.
  2. Dodaj do zaznaczenia i czekaj na niego również
  3. Powiadamianie przez pisanie do innego gniazda z innych wątków.
  4. Select natychmiast obudzić się jako jedno z gniazd jest czytelny, odczytuje wszystkie dane z w tym specjalnym gnieździe i sprawdzić wszystkie dane w kolejkach do wysyłania/recv

Jak stworzyć parę gniazd pod Windows ?

inline void pair(SOCKET fds[2]) 
{ 
    struct sockaddr_in inaddr; 
    struct sockaddr addr; 
    SOCKET lst=::socket(AF_INET, SOCK_STREAM,IPPROTO_TCP); 
    memset(&inaddr, 0, sizeof(inaddr)); 
    memset(&addr, 0, sizeof(addr)); 
    inaddr.sin_family = AF_INET; 
    inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); 
    inaddr.sin_port = 0; 
    int yes=1; 
    setsockopt(lst,SOL_SOCKET,SO_REUSEADDR,(char*)&yes,sizeof(yes)); 
    bind(lst,(struct sockaddr *)&inaddr,sizeof(inaddr)); 
    listen(lst,1); 
    int len=sizeof(inaddr); 
    getsockname(lst, &addr,&len); 
    fds[0]=::socket(AF_INET, SOCK_STREAM,0); 
    connect(fds[0],&addr,len); 
    fds[1]=accept(lst,0,0); 
    closesocket(lst); 
} 

Oczywiście należy dodać pewne kontrole dla zwracanych wartości.

+0

'(char *) i tak, sizeof (yes)' ?? nowy dla mnie :) – sarnold

+0

@sarnold W odróżnieniu od POSIX setsockopt, Windows one otrzymuje 'char const *' jako wartość zamiast 'void const *' jak na platformach POSIX. – Artyom

+2

Nie potrzebujesz naprawdę pary gniazdek, możesz użyć pojedynczego gniazda UDP, "podłączonego" do siebie. – Hasturkun

7

select nie jest natywnym interfejsem API dla systemu Windows. Natywny sposób to WSAWaitForMultipleEvents. Jeśli użyjesz tego do utworzenia wyczekiwanego oczekiwania, możesz użyć polecenia QueueUserAPC, aby poinformować wątek oczekujący o wysłaniu danych. (Może to również oznaczać, że nie musisz implementować własnej kolejki wyjściowej).

+1

Sugeruje się użycie IOCP, który ma wiele wad i ... chyba że poradzisz sobie ze 10000 połączeń, lepiej ich unikać. – Artyom

+0

Cóż, IOCP są jednym ze sposobów na wyjście z WSAWaitForMultipleEvents. Możesz także dodać Wydarzenie do listy oczekujących obiektów i zasygnalizować to zdarzenie, gdy są dane do wysłania. Nie pamiętam jednak problemów związanych z połączeniem WaitForMultipleEvents/QueueUserAPC, dlatego zasugerowałem to. – MSalters

+1

Artem, jakie są "liczne wady" IOCP? Nie żebym sugerował, że są one rozwiązaniem pierwotnego pytania lub tego konkretnego problemu; Ciekawi mnie tylko to, co według Ciebie jest trudne w komunikacji opartej na IOCP. –

0

Typowy model dla pracownika do obsługi własnego zapisu. Czy jest jakiś powód, dla którego chcesz wysłać cały wątek wyjściowy-IO do select?

Jeśli jesteś pewien tego modelu, możesz poprosić swoich pracowników o przesłanie danych z powrotem do wątku głównego za pomocą deskryptorów plików (pipe(2)) i po prostu dodaj te deskryptory do swojego połączenia select().

Jeśli jesteś szczególnie pewny, że nie będziesz używać rur do przesyłania danych z powrotem do głównego procesu, połączenie select pozwala określić limit czasu. Możesz zająć się czekaniem podczas sprawdzania wątków roboczych i okresowo dzwonić pod numer select, aby dowiedzieć się, z których gniazd TCP należy odczytać.

+0

Windows nie ma do wyboru rur – Artyom

+0

Ah. Zakładam, że Windows zaimplementował specyfikację POSIX dokładnie. Dzięki za notatkę. – sarnold

+1

"Windows zaimplementował specyfikację POSIX dokładnie" Ohhhhh, rozśmieszyłeś mnie. :) – Artyom

0

Innym szybkim rozwiązaniem jest dodanie do zestawu gniazd localhost. Teraz użyj tych gniazd jako kolejek komunikacji między wątkami. Każdy wątek roboczy wysyła coś do swojego gniazda, które kończy się na odpowiednim gnieździe w wątku odbiorczym. To budzi select(), a twój odbierający wątek może następnie wywołać echo wiadomości na odpowiednim gnieździe wychodzącym.

+0

W rzeczywistości zdarzenie WSAWaitForMultipleEvents wydaje się być mniej lub bardziej poprawne, ale muszę przeczytać kilka podręczników na temat korzystania z niego przed podjęciem decyzji –

4

Zobacz także tego posta: How to signal select() to return immediately?

dla systemu UNIX, należy użyć anonimowego rury. W przypadku systemu Windows: Odblokowanie można uzyskać, dodając do fd_set puste (niezwiązane) gniazdo datagramów, a następnie je zamykając. Aby uczynić ten wątek bezpiecznym, należy użyć funkcji QueueUserAPC:

Jedynym sposobem, w jaki znalazłem to zabezpieczenie wielowątkowe, jest zamknięcie i ponowne utworzenie gniazda w tym samym wątku, co instrukcja select. Oczywiście jest to trudne, jeśli wątek blokuje zaznaczenie. A następnie przychodzi w wywołaniu Windows QueueUserAPC. Gdy okna są blokowane w instrukcji select, wątek może obsłużyć asynchroniczne wywołania procedur. Możesz zaplanować to z innego wątku za pomocą QueueUserAPC. Windows przerywa wybieranie, wykonuje swoją funkcję w tym samym wątku i kontynuuje instrukcję select. Możesz teraz w swojej metodzie APC zamknąć gniazdo i odtworzyć je. Gwarantowana bezpieczna nici i nigdy nie stracisz sygnału.