Как отменить ожидание в select () в Windows - PullRequest
7 голосов
/ 26 июля 2010

В моей программе есть один поток (принимающий поток), который отвечает за прием запросов от сокета TCP, и есть много потоков (рабочих потоков), которые отвечают за обработку полученных запросов. После обработки запроса мне нужно отправить ответ по TCP.

А вот и вопрос. Я хотел бы отправить данные TCP в том же потоке, который я использую для получения данных. Этот поток после получения данных обычно ожидает новых данных в select(). Поэтому, как только рабочий поток завершает обработку запроса и помещает ответ в очередь вывода, он должен сигнализировать принимающему потоку о наличии данных для отправки. Проблема в том, что я не знаю, как отменить ожидание в select(), чтобы выйти из ожидания и позвонить send().

Или я должен использовать другой поток исключительно для отправки данных по TCP?

Обновлено

MSalters, Артём спасибо за ответы!

MSalters, прочитав ваш ответ, я нашел этот сайт: Методы ввода-вывода Winsock 2 и прочитал о WSAWaitForMultipleEvents(). Моя программа на самом деле должна работать как на HP-UX, так и на Windows. Наконец я решил использовать подход, предложенный Артемом.

Ответы [ 5 ]

16 голосов
/ 26 июля 2010

Вам нужно использовать что-то похожее на трюк с безопасным каналом, но в вашем случае вам нужно использовать пару подключенных TCP-сокетов.

  1. Создайте пару сокетов.* Добавьте один к выбору и дождитесь его также
  2. Уведомление путем записи в другой сокет из других потоков.
  3. Выбор немедленно активируется, поскольку один из сокетов доступен для чтения, читает вседанные в этом специальном сокете и проверить все данные в очередях для отправки / recv

Как создать пару сокетов под 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);
}

Конечно, некоторые проверки должны быть добавленыдля возвращаемых значений.

7 голосов
/ 26 июля 2010

select не является родным API для Windows. Собственный путь - WSAWaitForMultipleEvents . Если вы используете это для создания предупреждающего ожидания, вы можете использовать QueueUserAPC для указания ожидающему потоку отправки данных. (Это также может означать, что вам не нужно создавать собственную очередь вывода)

4 голосов
/ 30 декабря 2012

Смотрите также этот пост: Как подать сигнал select () для немедленного возврата?

Для unix используйте анонимный канал. Для Windows: Разблокировка может быть достигнута путем добавления фиктивного (несвязанного) сокета дейтаграммы в fd_set и затем его закрытия. Чтобы сделать этот поток безопасным, используйте QueueUserAPC:

Единственный способ сделать этот многопоточный безопасный способ - закрыть и воссоздать сокет в том же потоке, в котором выполняется оператор select. Конечно, это сложно, если поток блокирует выбор. И тогда в окнах приходит вызов QueueUserAPC. Когда в операторе select блокируется windows, поток может обрабатывать асинхронные вызовы процедур. Вы можете запланировать это из другого потока, используя QueueUserAPC. Windows прерывает процесс выбора, выполняет вашу функцию в том же потоке и продолжает выполнение оператора выбора. Теперь вы можете в своем методе APC закрыть сокет и воссоздать его. Гарантированная безопасность потока, и вы никогда не потеряете сигнал.

0 голосов
/ 26 июля 2010

Другое быстрое и грязное решение - добавить локальные сокеты в набор. Теперь используйте эти сокеты как очереди связи между потоками. Каждый рабочий поток просто отправляет что-то в свой сокет, который попадает в соответствующий сокет в вашем принимающем потоке. Это пробуждает select(), и ваш принимающий поток может затем повторить сообщение в соответствующем исходящем сокете.

0 голосов
/ 26 июля 2010

Типичная модель для работника - обрабатывать собственное письмо. Есть ли причина, по которой вы хотите отправить все выходные данные через select ing thread?

Если вы уверены в этой модели, ваши работники могут отправить данные обратно в главный поток, используя также файловые дескрипторы (pipe(2)), и просто добавить эти дескрипторы к вашему вызову select().

И, если вы особенно уверены, что не собираетесь использовать каналы для отправки данных обратно в ваш главный процесс, вызов select позволяет указать время ожидания. Вы можете подождать, проверяя рабочие потоки, и периодически вызывать select, чтобы выяснить, из каких сокетов TCP нужно читать.

...