OpenMP取消部分 [英] OpenMP cancel section

查看:93
本文介绍了OpenMP取消部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在终止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类型的全局变量,并且只能调用

This is actually super complicated, but let's just start simple.

  • It's #pragma omp cancellation point sections / #pragma omp cancel sections (mind the s).

  • You cannot use #pragma omp cancel across function boundaries.1

Let's say you could use cancel this way, cancellation is only checked for at specific cancellation points. So during a blocking read or getc, your threads will not be interrupted by a cancellation.

Signal handling

Signal handlers are setup per process, it is not deterministic at what thread a signal ends at. You should not try to call signal concurrently from multiple threads. Some implementations of signal even like multi-threaded programs at all. Instead, you should use sigaction, but still setup a global signal handler once before even spawning the worker threads.

There are certain restrictions on what you are allowed to do in a signal handler. Basically you must not access any global variables that are not of type volatile sig_atomic_t and call only async-signal-safe functions. You violate that in every single line of your signal handler.

In particular, you call send(client_socket) while either the same thread might just be interrupted while calling read(client_socket)2 or another thread calling read(client_socket) concurrently. Not sure what is worse, but even if send itself is async-signal-safe, I'd wager a wild guess, it's not safe in the suggested manner.

You see, having some reachable memory at the end of the process is absolutely the very least of your problems. You're not even allowed to call exit (you can call _exit though).

The usual way out, is to set make a global cancel flag of type volatile sig_atomic_t and set that within the signal handler as well as check for it in the worker loops. This should also work with OpenMP section/threads, but I would advise to add a #pragma omp atomic read/write seq_cst for any read/write to the flag. That may seem redundant, but I'm fairly sure that volatile sig_atomic_t only guarantees atomicity regarding interruption for signals, and not multithreading and in particular storage visibility. Unfortunately, you still have the issue that read and getc are blocking...

A sketch of a solution

So you would have to use some mechanism to make getc non-blocking or add a timeout to give your thread a chance to check for the cancel flag.

poll can give you a more elegant way out. You can replace the blocking part of both read and getc with poll - keep in mind however that this forces you to use stdin exclusively as a file descriptor, never as a FILE*. In preparation you make a pipe for each section whose output you include in the respective poll. Within the signal handler, that you setup only after the pipes is generated, you write to those pipes, indicating that the threads should shut down. If poll shows activity for those particular stop-pipe-fds, you exit the loop, and do your cleanup in the appropriate thread or after the parallel region. Apply synchronization as necessary.

Sorry to bring you the bad news. It's complicated and there is no simple, correct, copy-pasteable, solution. In any case, OpenMP cancellation is not the right thing to use here.

1: The standard is not quite explicit about that, it mandates that:

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.

However, since cancel regions are itself implicit cancellation points, I suppose one might imply that cancel constructs must always be within the lexical scoped of a explicit parallel region. Anyway, gcc won't let you compile a function like your signal handler with a cancel construct.

2: This is actually fine, but you have to at least manually restore errno because the write in your signal handler overwrites it. The read should conveniently return with EINTR.

这篇关于OpenMP取消部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆