如何停止一个正在运行的pthread线程?

7

如何立即退出或停止线程?

当用户输入答案时,我该如何使其立即停止?我希望它可以在每个问题后重置。

这是涉及线程的代码:

int q1() {
    int timer_start;
    char ans[] = "lol";
    char user_ans[50];
    timer_start = pthread_create( &xtimer,NULL,(void*)timer_func,(void*)NULL);
    printf("What is the capital city of Peru?\n");

    while(limit){
        scanf("%s",user_ans);
        if(limit)
        {
             if(!strcmp(user_ans, ans))
              {

               // printf("YAY!\n");
                score++;
               // q2();

            }
            else
            {
                game_over();
            }
        }
    }
}
4个回答

11
您可以简单地在该线程上调用pthread_cancel以退出它。您可以通过pthread_kill发送SIGSTOP / SIGCONT信号来停止/重新启动它。
但是,如果您只需要一个定时器,为什么一定要使用线程?

在这种情况下,如果您想在超时时触发某些内容,则应使用POSIX计时器。如果您只是想计算持续时间,则gettimeofday就足够了。请使用@xDianneCodex。 - Naruil
@xDianneCodex 不想再强调这一点,但时间无论如何都在流逝。通过 gettimeofday()(POSIX标准,但所有系统都会有类似的东西)可以访问系统时钟,它与您实现的任何其他计时方式一样准确。您在A点获取时间,在B点获取时间,B-A = 在两者之间发生的任何事情所经过的时间。 - CodeClown42
1
发送SIGSTOP/SIGCONT会影响整个进程,在Linux上至少如此。参考链接 - yyny

9

根据你的代码,我可以给出一个简单的答案:

在这种情况下根本不需要使用线程。

你不需要它们。存储开始时间,让用户回答问题,在用户回答后再次检查时间。

{
  time_t startTimeSec = time(NULL);

  // answering

  time_t endTimeSec = time(NULL);
  time_t timeTakenSec = endTime-startTime;
  if (timeTaken > 10) { 
    // do your thing
  }
}

回答你的问题:

在线程间异步通信时,应使用互斥锁保护或者volatile变量。一个线程设置该变量的值,另一个线程检查它的值。然后重置该变量的值,并重复这个过程。以下是一个简单的代码片段:

int stopIssued = 0;
pthread_mutex_t stopMutex;

int getStopIssued(void) {
  int ret = 0;
  pthread_mutex_lock(&stopMutex);
  ret = stopIssued;
  pthread_mutex_unlock(&stopMutex);
  return ret;
}

void setStopIssued(int val) {
  pthread_mutex_lock(&stopMutex);
  stopIssued = val;
  pthread_mutex_unlock(&stopMutex);
}

使用pthread_cancel()是一种选择,但我不建议这样做。您将需要在此调用返回后检查线程状态,因为pthread_cancel()不会等待实际线程停止。而且,在我看来更重要的是,我认为这样做很难看。


在这种情况下,@xDianneCodex是错误的。在我看来,只有当你真正知道自己在做什么时才应该使用线程。而且在我看来,你似乎是一个初学者。线程是一个非常困难的概念。它们可能看起来很简单,但相信我,有许多陷阱。 - Dariusz
7
如果你只是计时用户回答的时间,Dariusz是正确的。为此使用线程会很繁琐和低效。这有点像使用第二辆车跟踪第一辆车的行驶路程,而两辆车上都有里程表,这样做是没有必要的。你只需要检查第一辆车的里程表就可以了。第二辆车是完全多余的。 - CodeClown42
同意@Dariusz的观点。除非您的计时器逻辑中有一些复杂的逻辑,否则您应该在此处使用简单的Posix计时器。 - Naruil
程序有多个问题,用户需要在仅有的6秒钟内回答这些问题。 - xDianneCodex
+1 ;) 如果你只需要秒级的精度,那么time()可能比我建议的gettimeofday更好。 - CodeClown42
我不知道如何使用时间。 - xDianneCodex

5

使用方法来停止线程是一种粗暴的方式。相反地,您应该通过发出信号礼貌地要求线程停止。这样,线程将有一个选项来清理自己的东西,例如如果它已经分配了内存,而如果线程被取消,则不会有任何机会执行此操作。

该方法相对简单,不涉及操作系统信号:

在线程外部定义一个线程状态变量或结构体。在pthread_create中指向它,并在线程中取消引用状态变量。

int thread_state = 0; // 0: normal, -1: stop thread, 1: do something

static void *thread_1 (void *arg)
{
   int* pthread_state = arg;
   ... // initialize the thread locals
   while(1)
   {
      switch( *pthread_state )
      {
      case 0: // normal thread loop
         ...
         break;
      case -1:
         ... // tidy or whatever is necessary
         pthread_exit(0); // exit the thread signalling normal return
         break;
      case 1: //
         ... // do something special
         break;
      }
   }
}

pthread_create (&t_1, NULL, thread_1, (void*)&thread_state);

...

thread_state = -1; // signal to the thread to stop

// maybe use pthread_exit(0) to exit main.
// this will leave the threads running until they have finished tidy etc.

如果使用简单的“原子”变量或建立简单的握手机制,甚至可以使用结构与线程进行通信。否则可能需要使用互斥锁。 使用pthread_join等待线程终止。


0

@Naruil的建议是调用pthread_cancel(),这几乎是我找到的最好的解决方案,但如果您没有执行以下操作,它将无法正常工作。

根据 pthread_cancel的man-page,pthread_cancelibility取决于两个因素

  1. thread_cancel_state。
  2. thread_cancel_type。

thread_cancel_state默认为PTHREAD_CANCEL_ENABLE,因此我们主要关注thread_cancel_type,它的默认值为类型PTHREAD_CANCEL_DEFFERED,但我们需要在该线程上设置PTHREAD_CANCEL_ASYNCHRONOUS,以便取消。

以下是一个示例:

#include <stdio.h>
#include <pthread.h>

void *thread_runner(void* arg)
{   
    //catch the pthread_object as argument
    pthread_t obj = *((pthread_t*)arg);

    //ENABLING THE CANCEL FUNCTIONALITY
    int prevType;
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &prevType);

    int i=0;
    for( ; i < 11 ; i++)//1 - > 10
    {
        if(i == 5)
            pthread_cancel(obj);
        else
            printf("count -- %d", i);
    }
    printf("done");
}

int main(int argc, char *argv[])
{
    pthread_t obj;

    pthread_create(&obj, NULL, thread_runner, (void*)&obj);

    pthread_join(obj, NULL);

    return 0;
}

使用gcc filename.c -lpthread运行它,并输出以下内容: 计数 -- 0 计数 -- 1 计数 -- 2 计数 -- 3 计数 -- 4

请注意,由于线程在i变为5时被取消并且正在运行的线程被取消,因此done永远不会被打印。特别感谢@Naruil提供的"pthread_cancel"建议。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接