【问题标题】:Which is the correct way of cancelling main() thread from signal_handler routine?从 signal_handler 例程中取消 main() 线程的正确方法是什么?
【发布时间】:2023-03-27 05:19:02
【问题描述】:

我正在编写一个多线程服务器应用程序,它会回显客户端发送的任何内容。我为每个新客户生成一个线程。我已经使用while(1) 循环无限期地处理连续的客户端(并非完全无限期,我已将其强制限制为 64,之后它拒绝任何新连接)。现在我需要在我的服务器应用程序中处理 Ctrl+C 信号。

一个问题是它的not recommended 在多线程应用程序中使用signal()

但是,即使我使用method already discussed at SO 来捕获SIGINT 信号(来自 Ctrl+C),这也是我需要做的:
1) 服务器不应再接受更多客户端。
2) 与服务器的当前连接应该继续,除非客户端选择断开连接。

我的信号处理函数是:

void Ctrl_C_handler(int sig)
{
    if(sig == SIGINT)       // Ctrl+C
    {
        printf("Ctrl+C detected by server !!\n");
        printf("No more connections will be accepted!!");
        pthread_cancel(main_thread_id);
    }
}

我建议在 main() 中使用的取消线程的方法是:

// Method 1
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
while(1)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);

            // ..
            // client handling
            // ..
}

// Method 2

while(1)
{   
    pthread_testcancel();       // Create Cancellation point
            // ..
            // client handling
            // ..
}

// Method 3

while(1)
{
    pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
    pthread_testcancel();
    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
            // ..
            // client handling
            // ..
}

原因为什么我使用pthread_cancel()

根据this link,线程最终将使用pthread_exit() 终止(满足要求1)。由于NOTES section of pthread_exit() 解释它允许其他线程继续执行(满足要求2)。

如果我一直到这里,

哪种方法最适合取消线程。
我选择了方法3,因为:

(缺点)方法 1:如果线程分配内存,异步线程取消是不安全的(正如我在 SO 中的答案中看到的那样),以及其他几个原因

(缺点)方法2:根据this link,还有很多其他函数也可以作为取消点,其中一些我将在我的代码中使用。

(优点)方法3:与方法2类似,但更安全,因为线程被取消禁用,其他取消点将不活动。

请告诉我我的方法是否正确?错了,除了使用 pthread_cancel() 之外,还有其他更好的方法来处理 Ctrl+C 吗?

【问题讨论】:

    标签: c multithreading signals x86-64 suse


    【解决方案1】:

    我建议避免取消。

    相反,注册一个 SIGINT 处理程序,该处理程序 close()s 服务器的侦听套接字。 (为此使用sigaction。)在套接字关闭后,主线程可以正常退出,让所有客户端线程继续运行。

    例如在伪代码中:

    static int listen_fd = -1               // server socket
    
    void handle_SIGINT(int s):              // Requirement 1
      if listen_fd >= 0:
        close(listen_fd)
        listen_fd = -1
    
    int main(...):
      listen_fd = new_listening_socket(...)
      block_signal(SIGINT)
      trap_signal(SIGINT, handle_SIGINT)    // again, calls sigaction
    
      while 1:
        unblock_signal(SIGINT)
        new_client = accept(listen_fd)
        block_signal(SIGINT)
    
        if new_client < 0:                  // Requirement 1, also general error handling
          break
    
        spawn_worker_thread(new_client)     // probably attr PTHREAD_CREATE_DETACHED
    
      pthread_exit(NULL)                    // Requirement 2
      return 0                              // not reached
    

    您当然可以根据需要包含更细微的错误处理、清理例程等。

    【讨论】:

    • 我也是这么想的,pthread_cancel() 不应该在正常情况下使用,但无法想出更优雅的东西(就像你的方法一样),感谢您的解决方案。跨度>
    • 我还找到了similar code here。此代码使用完全不同的线程进行信号处理。
    • 一个简单的问题 我在使用这个方法时注意到:当accept()被调用时,它可能会复制listen_fd的值,并用它来接受客户端。因此,即使listen_fd被signal_handler关闭,而主线程被阻塞(被accept()),accept()调用可能会成功返回,从而得到正确的client_fd。解决方案:在accept() 之后,还要检查listen_fd 的有效性。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-11-20
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多