Эта проблема возникает только тогда, когда для сокета установлено время ожидания с SO_RCVTIMEO.
recv
должен блокировать на 3 сек. Но он возвращается из-за EINTR, как только начинается другой поток.
Если я запускаю поток t2
, recv
в потоке t1
вернусь -1
без блокировок и наборов errno
в EINTR
,
Но recv
в потоке t1
работает нормально, когда поток t2
не запускается, просто блокируется на 3 секунды.
Если нить t2
работает до потока t1
, recv
также работает правильно.
Я обнаружил, что это терпит неудачу каждый раз, когда я отлаживал с SlickEdit или GDB. Но работает правильно, когда работает в терминале.
Вот код:
test.cpp: ссылка -pthread
использовать <thread>
или поток создает исключение
#include<unistd.h>
#include<netdb.h>
#include<string.h>
#include<thread>
int socket_fd;
sockaddr_in server_addr;
void recvThread()
{
char pData[4096];
int len = recv(socket_fd,pData,4096,0);
if(len<=0)
{
printf("len:%d\n",len);
printf("errno:%d\n",errno);
}
}
void otherThread()
{
while(1)
{
sleep(1);
}
}
int main()
{
hostent *host;
if((host=gethostbyname("127.0.0.1"))==NULL)
{
return 1;
}
memset(&server_addr, 0, sizeof(sockaddr_in));
server_addr.sin_family=AF_INET;
server_addr.sin_port=htons(8887);
server_addr.sin_addr=*((in_addr*)host->h_addr);
bzero(&(server_addr.sin_zero),8);
socket_fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
timeval timeout = {3,0};
setsockopt(socket_fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeval));
if(connect(socket_fd, (sockaddr*)&server_addr, sizeof(server_addr))<0)
{
return 1;
}
std::thread t1(recvThread);
std::thread t2(otherThread);
t1.join();
t2.join();
}
См. Комментарий к EINTR от «Некоторый программист чувак»
Отладчик (gdb), если он не может устанавливать / изменять точки останова асинхронно, должен остановить цель (вашу задачу), установить точки останова, а затем возобновить его.
Чтобы остановить его, он может отправить SIGINT, что приведет к блокировке вызовов EINTR в вашей системе.
Если вы используете библиотеку GNU C, вы можете использовать макрос TEMP_FAILURE_RETRY, см. Этот пост:
TEMP_FAILURE_RETRY и __USE_GNU