【问题标题】:How do I terminate a thread that is waiting for a semaphore operation如何终止正在等待信号量操作的线程
【发布时间】:2010-11-11 14:16:22
【问题描述】:

我正在编写一个使用共享内存和 ipc 信号量的程序。有一个主服务器进程创建共享内存和信号量。任何数量的客户端进程都可以附加到共享内存并在允许时对其进行读写。信号量提供阻塞机制来控制读取和写入。一切正常,除非我尝试终止客户端。访问共享内存的信号量块位于线程中,并且在进程终止时,我无法释放信号量块,因此线程正确退出。我该怎么办?这是针对 Linux 的。

具体来说,有一个 shm 和两个 sem。第一个 sem 阻止写入,第二个阻止读取。当客户端有东西要写时,它会等待 write sem 为 0,然后将其设置为 1,写入,然后将 read sem 设置为 0,这会释放等待的服务器以读取客户端写入的内容。一旦读取,服务器将 write sem 设置回 0,并且下一个客户端开始写入。它挂在读取 sem 为 0 时释放的 semop 调用上。这个 semop 调用在一个线程中,我需要弄清楚如何在让主线程终止之前正确退出该线程。

这是我想做但不起作用的示例(睡眠假装是挂起的 semop 调用):

#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <unistd.h>

void termination_handler (int signum) {
    printf( "Got Signal\n" );
}

void *threadfunc( void *parm ) {
    struct sigaction action;

    action.sa_handler = termination_handler;
    sigemptyset( &action.sa_mask );
    action.sa_flags = 0;

    sigaction( SIGUSR1, &action, NULL );

    printf("Thread executing\n");

    sleep( 100 ); // pretending to be the semaphore

    pthread_exit( NULL );
}

int main() {
    int       status;
    pthread_t threadid;
    int       thread_stat;

    status = pthread_create( &threadid, NULL, threadfunc, NULL );

    if ( status <  0) {
        perror("pthread_create failed");
        exit(1);
    }

    sleep( 5 );

    status = pthread_kill( threadid, SIGUSR1 );

    if ( status <  0 )
        perror("pthread_kill failed");

    status = pthread_join( threadid, (void *)&thread_stat );
    if ( status <  0 )
        perror("pthread_join failed");

    exit( 0 );
}

【问题讨论】:

  • 请详细说明“无法释放信号量块”的含义。为什么?
  • 所以,抛开线程终止问题不谈,真正的问题是为什么你要阻塞这么长时间等待共享内存访问。据推测,客户端正在等待服务器将 write sem 设置回他们可以解除阻塞的位置。服务器是在执行一些长时间的任务,还是设计中可能存在一些未诊断的死锁问题?
  • 好吧,进一步阐述。逻辑或等待期没有问题。每个客户端也有一个挂起的 shm,直到服务器向该进程 shm 写入一些内容,并且服务器有一个挂起的 shm,直到任何客户端向该进程 shm 写入一些内容。所以你可以看到,当什么都没有发生时,所有的客户端和服务器只是坐等。我实际上正在研究使用标准输入或内存队列。与其他方法相比,这种原始方法似乎过于复杂。
  • 啊,情节变厚了。 :) FWIW,我认为 shm 是一个 PITA,除了最关键的任务之外,在这种情况下你等待了很多。如果您有重新编码的奢侈,Posix MQ 在这里似乎是一个不错的选择。每一方都可以将数据写入队列而不会阻塞,并且可以在读取方使用 mq_notify() 之类的东西。在不知道应用程序的详细信息(我怀疑还有更多)的情况下,它简化了许多事情。
  • 我想我实际上会使用管道并只处理标准文件描述符。玩弄它,它做得很好,而且非常简单。感谢所有的帮助和投入。如果您对管道有任何异议,请告诉我,我希望这是我最后一次重新编码!

标签: c linux ipc pthreads semaphore


【解决方案1】:

他说,这是给 Linux 的。

如果你能准确地说出你是怎么做的,那将会很有用。我假设您在 sem_wait 或 sem_timedwait 中阻塞。如果你的线程在那里阻塞并且你想中断它,你可以使用 pthread_kill。

pthread_kill(blocking_thread_id, SIGUSR1);

当然,您需要设置正确的信号处理程序(man sigaction)来捕获 SIGUSR1,并且您需要检查 sem_wait() 的返回码是否为 EINTR,在这种情况下,您可以做任何您想做的事情,知道您被打断了,没拿到锁。

如果您正在使用进程,您只需使用 kill() 而不是 pthread_kill() 提供进程 ID。 (对不起,一开始我看错了,以为你在用线程)

【讨论】:

  • 顺便说一句,ianion 使用的是 SysV 而不是 POSIX 信号量,即semop() 而不是sem_wait() 和朋友们。
【解决方案2】:

根据您的环境,您可能只能尝试使用超时的信号量。每次超时后检查是否已请求关闭线程并简单地放弃并关闭。

【讨论】:

    【解决方案3】:

    我有两个半的答案给你。 :)

    首先,您的示例代码适用于我(在 Linux 上):pthread_kill 成功 EINTRupts 工作线程的 sleep 符合预期大约 5 秒后,显示了几个 printfs 并记住了 sleep 的返回值。 AFAICT,如果你想用信号中断一个特定的线程,你已经做到了。

    其次,试试SEM_UNDO。该标志可以设置在 sembuf 参数 semop 中传递的 sem_flg 成员中,顾名思义,它将撤消信号量调整进程终止。 IIUC,当您杀死一个客户端时,该客户端会不恰当地锁定信号量。 SEM_UNDO 就是为这种情况而设计的。

    最后,恭敬地,您是否在这里颠倒了信号量的逻辑?当我阅读您的问题时,零的 semval 表示“无资源”,而 semval 的一是“资源锁定”(引用:“...[a client] 等待写入 sem 为 0,然后设置它为 1,写入...")。但是,如果两个或更多写入客户端正在等待 SysV sem 降为零,那么当这种情况发生时,它们将全部被释放。这是一种令人不快的竞争条件,至少可能会导致意外的信号量减少和增加。

    【讨论】:

    • 我不认为当信号量降为零时,所有被阻止的 semops 都会在我的实现中释放。我让每个 semop 调用执行两个操作,第一个执行等待零,第二个将其设置回一个。我的理解是,如果我有 5 个这样的操作排队等待零,一旦信号量下降到零,它在所有等待的 semop 调用上都是一个 FIFO,因此五个中的第一个执行这两个操作,当你到达队列中的第二个信号量不再为零,因此它再次等待。这对我来说似乎很好。
    • 啊,我没想到一个semop() 执行了“等待零”和“发布”。 (你在某处有完整的示例代码吗?)是的,这会起作用,尽管我承认我觉得这种用法有点尴尬。谢谢。
    【解决方案4】:

    如果对受保护区域的操作对您的目的而言可能持续时间过长,则使用阻塞互斥体/信号量可能不是最佳主意。

    您可以通过将读写请求放入队列(例如链表)并让队列中的第一个对受保护区域进行操作并在进入该区域后将其从列表中删除来解决此问题。

    在只读操作的情况下,只要第一个操作是只读的,您就可以访问其他进入受保护区域的读取。当第一个操作是写时,保护区域必须是空的,然后才允许它访问。

    列表修改必须受到互斥锁(或类似的东西)的保护,但这几乎是恒定的时间,您可能可以支付。

    当线程位于队列中时,每个线程都有其私有条件变量,您可以使用它来唤醒其中的任何一个。条件变量也必须受到互斥锁的保护。您可以将条件变量、互斥锁等存储到一个结构中,然后将它们放入一个数组或列表中,并存储每个线程的 id,这样就可以很容易地找到您想要唤醒的线程。

    一旦线程唤醒,它首先检查它必须唤醒的原因。如果设置了退出标志,则线程知道退出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-04-21
      • 1970-01-01
      • 2013-10-20
      • 1970-01-01
      • 2015-09-30
      • 1970-01-01
      • 2017-12-21
      相关资源
      最近更新 更多