您如何在不收到Etimedout错误的情况下正确使用PTHREAD_COND_TIMEDWAIT()?
我对 pthread 和 time 类非常陌生,目前正在做一项家庭作业,其中我必须使用 pthread_cond_timedwait() 命令在特定时间发送字符串数据包。该命令在声明为 sendPackets() 函数的线程中调用;将所有数据包发送到目标 IP 的函数。线程初始化得很好,但在存储了我希望线程解除阻塞的时间并将其用作 timedwait() 中的参数后,该函数返回 ETIMEDOUT 错误。现在我知道我的条件变量可能是(并且可能是)它超时的原因。我试图对这个函数进行研究,但无论我做了多少搜索,我都没有找到任何解决我的问题的方法(这可能是因为我忽略了一些简单的事情)。
互斥对象和 pthread_cond_t 对象被建立为全局变量。它们具有全局作用域,以便所有线程都可以访问它们。我还建立了一个结构体,以便保存有关我要发送的数据包集的信息:
struct info{
int socket;
int size;
int count;
float interval;
struct sockaddr_in echoServAddr;
};
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t thread = PTHREAD_COND_INITIALIZER;
读入 CLA 后(这些决定了数据包计数、间隔、字节大小和服务器端口等内容),我检查查看该程序是否被称为服务器或客户端(该程序应该可以互换,具体取决于标志 -S 的存在)。如果main方法是客户端,它会进入下面的if语句并初始化sendPackets()线程。创建并初始化信息指针并将其转换为 void 指针,以便将参数传递给 sendPackets() 函数。
if(isClient){
/*Create a datagram UDP socket*/
if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
DieWithError("socket() failed\n");
}
/* Construct the server address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr));
echoServAddr.sin_family = AF_INET;
echoServAddr.sin_addr.s_addr = inet_addr(servIP);
echoServAddr.sin_port = htons(echoServPort);
enum threads {sender=0,receiver};
struct info *packets = (struct info*)malloc(sizeof(struct info));
packets->size = size;
packets->count = ping_packet_count;
packets->socket = sock;
packets->echoServAddr = echoServAddr;
packets->interval = ping_interval;
pthread_t tid[2];
int a,b; //Thread creation return variables
a = pthread_create(&(tid[sender]),NULL,&sendPackets,(void*)packets);
pthread_join(tid[sender], NULL);
//pthread_join(tid[receiver], NULL);
pthread_mutex_destroy(&lock);
}
一旦线程开始,它就会获取锁并继续执行其代码。开始时间是程序开始处理数据包的时间。当前时间表示程序计算何时发送下一个数据包时所处的时间,发送数据包是开始时间+每个数据包的延迟(sendTime = start_time + [id#] * packet_interval)。在对代码进行了一些测试之后,我注意到程序直到 sendTime() 指定的时间才会超时,这进一步表明我只是对条件变量做了一些错误的事情,因为我对它们非常不熟悉。最后一点注意:clk_id 是我设置为 CLOCK_REALTIME 的宏。
void* sendPackets(void *p){
printf("Starting sendPackets function...\n");
pthread_mutex_lock(&lock);
printf("Sender has aquired lock\n\n");
struct info *packet = (struct info*)p;
printf("Packet Details: Socket: %d Size %d Count %d Interval:%f\n\n",packet->socket,packet->size,packet->count,packet->interval);
struct timespec startTime = {0};
for(int i = 0; i < packet->count; i++){
struct timespec sendTime = {0};
struct timespec currentTime = {0};
float delay = packet->interval * i;
int delayInt = (int) delay;
unsigned char echoString[packet->size];
char strbffr[200] = "";
inet_ntop(AF_INET,&(packet->echoServAddr.sin_addr.s_addr),strbffr,200*sizeof(char));
sendTime.tv_sec = 0;
sendTime.tv_nsec = 0;
printf("PacketID:%d Delay:%f DelayInt:%d\n",i,delay,delayInt);
if(i == 0){
clock_gettime(clk_id,&startTime);
startTime.tv_sec+=1;
}
clock_gettime(clk_id,¤tTime);
sendTime.tv_sec = startTime.tv_sec + delayInt;
sendTime.tv_nsec = startTime.tv_nsec + (int)((delay - delayInt) * 1000000000);
printf("startTime: tv_sec = %d tv_nsec = %d\n",(int)startTime.tv_sec,(int)startTime.tv_nsec);
printf("sendTime: tv_sec = %d tv_nsec = %d\n",(int)sendTime.tv_sec,(int)sendTime.tv_nsec);
printf("currentTime: tv_sec = %d tv_nsec = %d\n\n",(int)currentTime.tv_sec,(int)currentTime.tv_nsec);
int r_wait;
if((r_wait = pthread_cond_timedwait(&thread,&lock,&sendTime)) != 0){
clock_gettime(clk_id,¤tTime);
printf("currentTime: tv_sec = %d tv_nsec = %d\n\n",(int)currentTime.tv_sec,(int)currentTime.tv_nsec);
printf("Received error for timedwait:%s\n",strerror(r_wait));
exit(1);
}
if (sendto(packet->socket, echoString, packet->size, 0, (struct sockaddr *) &packet->echoServAddr, sizeof(packet->echoServAddr)) != packet->size){
DieWithError("sendto() sent a different number of bytes than expected\n");
}
printf("Sent %d to IP:%s\n",i,strbffr);
}
for(int i = 0; i < packet->count; i++){
unsigned char echoString[packet->size];
char strbffr[200] = "";
inet_ntop(AF_INET,&(packet->echoServAddr.sin_addr.s_addr),strbffr,200*sizeof(char));
if (sendto(packet->socket, echoString, packet->size, 0, (struct sockaddr *) &packet->echoServAddr, sizeof(packet->echoServAddr)) != packet->size){
DieWithError("sendto() sent a different number of bytes than expected\n");
}
printf("Sent %d to IP:%s\n",i,strbffr);
}
pthread_mutex_unlock(&lock);
printf("Sender has released lock\n");
printf("Yielding Sender\n\n");
sched_yield();
我知道这是很多需要考虑的内容。如果您想查看我的代码中我没有提到的任何其他部分,请随时发表评论,说明您想要什么喜欢看。我非常有信心这是我的代码中与该问题相关的每个数据结构,但是,我总是可能是错的。
I am really new to the pthread and time classes and I am currently doing a homework assignment in which I have to send packets of strings at specific times using the pthread_cond_timedwait() command. The command is called in a thread declared to the sendPackets() function; a function that will send all packets to the target IP. The thread initializes just fine but after storing the time that I would like the thread to unblock and uses it as an argument in timedwait(), the function returns the ETIMEDOUT error. Now I am aware that my condition variable could be (and probably is) the reason why it is timing out. I have tried to do research on this function but no matter how much searching I did I haven't found any solutions to my problem (and this is probably because of something simple I overlooked).
Established as global variables are the mutex object and the pthread_cond_t object. They have a global scope so that all threads can access them. I also have established a struct in order to hold information about the set of packets that i'm sending:
struct info{
int socket;
int size;
int count;
float interval;
struct sockaddr_in echoServAddr;
};
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t thread = PTHREAD_COND_INITIALIZER;
After the CLA's are read in (these determine things such as packet count, interval, size in bytes, and server port), I check to see if the program was called as a server or a client (the program is supposed to be interchangeable depending on the presence of the flag -S). If the main method is a client, it goes into the following if statement and initializes the sendPackets() thread. An info pointer is created and initialized and casted to a void pointer in order to pass arguments to the sendPackets() function.
if(isClient){
/*Create a datagram UDP socket*/
if((sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0){
DieWithError("socket() failed\n");
}
/* Construct the server address structure */
memset(&echoServAddr, 0, sizeof(echoServAddr));
echoServAddr.sin_family = AF_INET;
echoServAddr.sin_addr.s_addr = inet_addr(servIP);
echoServAddr.sin_port = htons(echoServPort);
enum threads {sender=0,receiver};
struct info *packets = (struct info*)malloc(sizeof(struct info));
packets->size = size;
packets->count = ping_packet_count;
packets->socket = sock;
packets->echoServAddr = echoServAddr;
packets->interval = ping_interval;
pthread_t tid[2];
int a,b; //Thread creation return variables
a = pthread_create(&(tid[sender]),NULL,&sendPackets,(void*)packets);
pthread_join(tid[sender], NULL);
//pthread_join(tid[receiver], NULL);
pthread_mutex_destroy(&lock);
}
Once the thread begins, it acquires the lock and proceeds to carry out its code. Start time is the time the program had begun processing packets. Current time represents the time that the program is at when calculating when to send the next packet, and send packet is the start time + the delay for each packet (sendTime = start_time + [id#] * packet_interval). After testing the code a bit, ive noticed the program doesn't time out until the time specified by sendTime(), which even further shows me that I am just doing something wrong with my condition variable since im so unfamiliar with them. Last little note: clk_id is a macro I had set to CLOCK_REALTIME.
void* sendPackets(void *p){
printf("Starting sendPackets function...\n");
pthread_mutex_lock(&lock);
printf("Sender has aquired lock\n\n");
struct info *packet = (struct info*)p;
printf("Packet Details: Socket: %d Size %d Count %d Interval:%f\n\n",packet->socket,packet->size,packet->count,packet->interval);
struct timespec startTime = {0};
for(int i = 0; i < packet->count; i++){
struct timespec sendTime = {0};
struct timespec currentTime = {0};
float delay = packet->interval * i;
int delayInt = (int) delay;
unsigned char echoString[packet->size];
char strbffr[200] = "";
inet_ntop(AF_INET,&(packet->echoServAddr.sin_addr.s_addr),strbffr,200*sizeof(char));
sendTime.tv_sec = 0;
sendTime.tv_nsec = 0;
printf("PacketID:%d Delay:%f DelayInt:%d\n",i,delay,delayInt);
if(i == 0){
clock_gettime(clk_id,&startTime);
startTime.tv_sec+=1;
}
clock_gettime(clk_id,¤tTime);
sendTime.tv_sec = startTime.tv_sec + delayInt;
sendTime.tv_nsec = startTime.tv_nsec + (int)((delay - delayInt) * 1000000000);
printf("startTime: tv_sec = %d tv_nsec = %d\n",(int)startTime.tv_sec,(int)startTime.tv_nsec);
printf("sendTime: tv_sec = %d tv_nsec = %d\n",(int)sendTime.tv_sec,(int)sendTime.tv_nsec);
printf("currentTime: tv_sec = %d tv_nsec = %d\n\n",(int)currentTime.tv_sec,(int)currentTime.tv_nsec);
int r_wait;
if((r_wait = pthread_cond_timedwait(&thread,&lock,&sendTime)) != 0){
clock_gettime(clk_id,¤tTime);
printf("currentTime: tv_sec = %d tv_nsec = %d\n\n",(int)currentTime.tv_sec,(int)currentTime.tv_nsec);
printf("Received error for timedwait:%s\n",strerror(r_wait));
exit(1);
}
if (sendto(packet->socket, echoString, packet->size, 0, (struct sockaddr *) &packet->echoServAddr, sizeof(packet->echoServAddr)) != packet->size){
DieWithError("sendto() sent a different number of bytes than expected\n");
}
printf("Sent %d to IP:%s\n",i,strbffr);
}
for(int i = 0; i < packet->count; i++){
unsigned char echoString[packet->size];
char strbffr[200] = "";
inet_ntop(AF_INET,&(packet->echoServAddr.sin_addr.s_addr),strbffr,200*sizeof(char));
if (sendto(packet->socket, echoString, packet->size, 0, (struct sockaddr *) &packet->echoServAddr, sizeof(packet->echoServAddr)) != packet->size){
DieWithError("sendto() sent a different number of bytes than expected\n");
}
printf("Sent %d to IP:%s\n",i,strbffr);
}
pthread_mutex_unlock(&lock);
printf("Sender has released lock\n");
printf("Yielding Sender\n\n");
sched_yield();
I am aware that this is a lot of stuff to take in. If there is any other part of my code that you would like to take a look at that I haven't mentioned then please feel free to post a comment stating what you would like to see. I'm pretty confident this is every data structure in my code that is relevant to the issue, however, I could always be wrong.
Here is an image of the output of my program from the print statements I have listed.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您似乎正在使用
PTHREAD_COND_TIMEDWAIT()
作为计时器:您不会期望CV发出信号(这会尽早终止等待),而是要暂停调用线程,以全面暂停。指定的超时。在这种情况下,
eTimedout
正是当一切按预期工作时,您应该期望的。您应该检查并接受它,如果您看到其他任何东西,则应执行适当的处理。特别是,Pthreads CV可以显示出虚假的唤醒,因此,如果您的pthread_cond_wait()
曾经正常返回,那么您需要循环循环并再次等待以确保在进行之前进行全超时时间。简而言之,您不应查看
eTimedout
返回代码,这表明出现了问题,而是(出于您的特定目的)一切都正确。You appear to be using
pthread_cond_timedwait()
as a timer: you don't expect the CV to be signaled (which would terminate the wait early), but rather for the calling thread to be suspended for the full specified timeout.In that case,
ETIMEDOUT
is exactly what you should expect when everything works as intended. You should check for that and accept it, and you should perform appropriate handling if you see anything else. In particular, pthreads CV's can exhibit spurious wakeup, so if yourpthread_cond_wait()
ever returns normally then you need to loop back and wait again to ensure that the full timeout elapses before you proceed.In short, you should not view an
ETIMEDOUT
return code as indicating that something went wrong, but rather that (for your particular purposes) everything went right.