我在终止C程序中的部分时遇到问题。在一个线程中捕获SIGINT
信号后,我想退出所有线程,但我真的不知道如何,因为我在这些循环中有无限循环。程序等待来自服务器或stdin的输入。所以我使用了信号处理程序。
我真的不知道我是否正确地这样做,而且我真的不明白OpenMP中的取消是如何工作的。我没有为此找到合适的教程或讲座。
我的任务是在捕获SIGINT
信号后终止程序。但是当我在处理程序中使用exit()
时,显然会留下未释放的内存。我会很高兴任何建议,谢谢。
#pragma omp parallel num_threads(2)
{
#pragma omp sections
{
#pragma omp section
{
void intHandler(int dummy)
{
char * welcome1 = calloc(strlen(username)+14,sizeof(char));
strcat(welcome1,username);
strcat(welcome1," logged out\r\n");
if(send(client_socket,welcome1,strlen(welcome1),0) < 0)
{
callError("ERROR: cannot send socked");
}
free(welcome1);
#pragma omp cancel section
}
signal(SIGINT, intHandler);
int i = 0, j = 1;
while(1)
{
str = (char*)malloc(sizeof(char));
while((c = getc(stdin)) != '\n')
{
str = (char*)realloc(str, j * sizeof(char));
str[i] = c;
i++;
j++;
}
str = (char*)realloc(str, j * sizeof(char));
str[i] = '\0';
if(strlen(str)!=0)
{
bufferIn = message(username,str);
if(send(client_socket,bufferIn,strlen(bufferIn),0) < 0)
{
callError("ERROR: cannot send socked");
}
free(bufferIn);
}
free(str); i = 0; j = 1;
}
#pragma omp cancellation point section
}
#pragma omp section
{
void intHandler(int dummy)
{
char * welcome1 = calloc(strlen(username)+14,sizeof(char));
strcat(welcome1,username);
strcat(welcome1," logged out\r\n");
if(send(client_socket,welcome1,strlen(welcome1),0) < 0)
{
callError("ERROR: cannot send socked");
}
free(welcome1);
#pragma omp cancel section
}
signal(SIGINT, intHandler);
char buffer[4096];
ssize_t length;
int received = 0;
int data_cap = 4096;
while(1)
{
data = calloc(BUFFER_LEN,sizeof(char));
while ((length = read(client_socket, buffer, BUFFER_LEN-1)) > 0)
{
received += length;
buffer[length] = '\0';
if (received > data_cap)
{
data = realloc(data,sizeof(char) * data_cap * 2);
data_cap = data_cap * 2;
}
strcat(data, buffer);
if(!isEnough(data))
{
break;
}
}
printf("%s", data);
free(data); bzero(buffer,BUFFER_LEN); data_cap = 4096; received = 0; length = 0;
}
#pragma omp cancellation point section
}
}
}
答案 0 :(得分:1)
这实际上非常复杂,但让我们开始简单。
#pragma omp cancellation point sections
/ #pragma omp cancel sections
(请注意s)。
您不能跨职能界限使用#pragma omp cancel
。 1
我们假设您可以使用取消方式取消,仅在特定取消点检查取消。因此,在阻止read
或getc
期间,您的线程不会被取消中断。
每个进程都会设置信号处理程序,但信号结束的线程不确定。您不应该尝试从多个线程同时调用signal
。 signal
的一些实现甚至就像多线程程序一样。相反,您应该使用sigaction
,但在生成工作线程之前仍然会设置一次全局信号处理程序。
在信号处理程序中允许执行的操作存在某些限制。基本上,您不能访问任何非volatile sig_atomic_t
类型的全局变量,只能调用async-signal-safe functions。你违反了信号处理程序的每一行。
特别是,在调用send(client_socket)
2 或同时调用read(client_socket)
的另一个线程时,同一线程可能被中断时,调用read(client_socket)
。不确定更糟糕的是,但即使send
本身是 async-signal-safe ,我也会猜测,但建议的方式并不安全
你知道,在流程结束时有一些可达的内存绝对是你的问题。您甚至不允许致电exit
(您可以致电_exit
)。
通常的方法是设置一个类型为volatile sig_atomic_t
的全局取消标志,并在信号处理程序中设置它,并在工作循环中检查它。这也适用于OpenMP部分/线程,但我建议为该标志的任何读/写添加#pragma omp atomic read/write seq_cst
。这似乎是多余的,但我相当确定volatile sig_atomic_t
仅保证信号中断的原子性,而不是多线程,特别是存储可见性。不幸的是,您仍遇到read
和getc
阻止的问题......
因此,您必须使用某种机制使getc
无阻塞或添加超时以使您的线程有机会检查取消标记。
poll
可以为您提供更优雅的出路。您可以将read
和getc
的阻止部分替换为poll
- 请记住,这会强制您将stdin专门用作文件描述符,而不是{{1} }。在准备中,您为每个部分创建一个管道,其输出包含在相应的FILE*
中。在信号处理程序中,只有在生成管道之后才设置,您可以写入这些管道,指示线程应该关闭。如果poll显示那些特定stop-pipe-fds的活动,则退出循环,并在相应的线程中或在并行区域之后进行清理。根据需要应用同步。
很抱歉给你带来坏消息。它很复杂,没有简单,正确,可复制粘贴的解决方案。在任何情况下,OpenMP取消都不适合在这里使用。
1 :标准对此并不十分明确,它要求:
在执行可能被取消的构造期间,线程不得遇到孤立的取消点。也就是说,只能在该构造中遇到取消点,并且不得在其区域的其他地方遇到。
然而,由于取消区域本身是隐式取消点,我想可能暗示取消构造必须始终在显式并行区域的词法范围内。无论如何,poll
不允许你使用取消构造编译像信号处理程序这样的函数。
2 :这实际上很好,但您必须至少手动恢复gcc
,因为信号处理程序中的errno
会覆盖它。 write
应该方便地返回read
。