gpt4 book ai didi

c - OpenMP 取消部分

转载 作者:太空宇宙 更新时间:2023-11-04 03:23:11 25 4
gpt4 key购买 nike

我在终止 C 程序中的部分时遇到问题。抓到 SIGINT 后在一个线程中发出信号我想退出所有线程,但我真的不知道怎么做,因为我在这些循环中有无限循环。程序等待来自服务器或标准输入的输入。所以我使用了信号处理程序。

我真的不知道我这样做是否正确,而且我真的不明白 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
}
}

}

最佳答案

这实际上非常复杂,但让我们从简单的开始。

  • 它是 #pragma omp cancellation point sections/#pragma omp cancel sections (注意 s)。
  • 您不能使用 #pragma omp cancel跨越功能边界。1

  • 假设您可以通过这种方式使用取消,仅在特定取消点检查取消。所以在阻塞 readgetc ,您的线程不会因取消而中断。

    信号处理

    信号处理程序是按进程设置的,信号在哪个线程结束并不确定。你不应该尝试调用 signal同时来自多个线程。 signal 的一些实现甚至像多线程程序一样。相反,您应该使用 sigaction ,但在产生工作线程之前仍然设置一次全局信号处理程序。

    在信号处理程序中允许您执行的操作有一定的限制。基本上你不能访问任何不属于 volatile sig_atomic_t 类型的全局变量。并且只调用 async-signal-safe functions .您在信号处理程序的每一行都违反了这一点。

    特别是,您调用 send(client_socket)而在调用 read(client_socket) 时,同一线程可能会被中断。 2 或另一个线程调用 read(client_socket)同时。不知道什么更糟,但即使 send本身是异步信号安全的,我敢打赌,建议的方式并不安全。

    您会看到,在流程结束时拥有一些可访问的内存绝对是您最不重要的问题。你甚至不被允许调用 exit (您可以调用 _exit)。

    通常的出路是设置 volatile sig_atomic_t 类型的全局取消标志。并在信号处理程序中设置它,并在工作循环中检查它。这也应该适用于 OpenMP 部分/线程,但我建议添加 #pragma omp atomic read/write seq_cst对标志的任何读/写。这似乎是多余的,但我相当肯定 volatile sig_atomic_t只保证关于信号中断的原子性,而不是多线程,特别是存储可见性。不幸的是,您仍然遇到 read 的问题。和 getc正在阻止...

    解决方案示意图

    所以你必须使用一些机制来制作 getc非阻塞或添加超时以使您的线程有机会检查取消标志。
    poll可以给你一个更优雅的出路。您可以更换两个 read 的阻塞部分和 getcpoll - 但是请记住,这会迫使您将 stdin 专门用作文件描述符,而不是作为 FILE* .在准备中,您为每个部分制作一个管道,其输出包含在相应的 poll 中。 .在仅在生成管道后设置的信号处理程序中,您写入这些管道,指示线程应该关闭。如果 poll 显示那些特定 stop-pipe-fds 的事件,则退出循环,并在适当的线程中或并行区域之后进行清理。根据需要应用同步。

    很抱歉给你带来坏消息。它很复杂,没有简单、正确、可复制粘贴的解决方案。无论如何,在这里使用 OpenMP 取消是不正确的。

    1:该标准对此并不十分明确,它要求:

    During execution of a construct that may be subject to cancellation, a thread must not encounter an orphaned cancellation point. That is, a cancellation point must only be encountered within that construct and must not be encountered elsewhere in its region.



    然而,由于取消区域本身就是隐式取消点,我想有人可能暗示取消构造必须始终在显式并行区域的词法范围内。无论如何, gcc不会让您使用取消构造编译像信号处理程序这样的函数。

    2:这个其实还可以,但是至少要手动恢复 errno因为 write在您的信号处理程序中覆盖它。 read应该方便地返回 EINTR .

    关于c - OpenMP 取消部分,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43496870/

    25 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com