如何从循环外部杀死无限循环中的pthread?

时间:2023-02-04 19:37:02

I create a thread and I put it into an infinite loop. I get memory leaks when checking the code with valgrind. Here is my code:

我创建了一个线程,并将其置于无限循环中。使用valgrind检查代码时出现内存泄漏。这是我的代码:

#include <pthread.h>
#include <time.h>

void thread_do(void){
    while(1){}
}

int main(){
    pthread_t th;   
    pthread_create(&th, NULL, (void *)thread_do, NULL);

    sleep(2);
    /* I want to kill thread here */
    sleep(2);
    return 0;
}

So a thread is created in main and just runs thread_do() all the time. Is there a way to kill it from inside main after 2 seconds? I have tried both pthread_detach(th) and pthread_cancel(th) but I still get leaks.

所以在main中创建一个线程,并且一直运行thread_do()。有没有办法在2秒后从主内部杀死它?我已经尝试过pthread_detach(th)和pthread_cancel(th),但我仍然会泄漏。

2 个解决方案

#1


34  

As @sarnold pointed out, by default your thread can't be cancelled with pthread_cancel() without calling any functions that are cancellation points... but this can be changed by using pthread_setcanceltype() to set the thread's cancellation type to asynchronous instead of deferred. To do that, you'd add something like pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); near the start of your thread function, before you start the loop. You would then be able to terminate the thread by calling pthread_cancel(th) from main().

正如@sarnold指出的那样,默认情况下你的线程不能用pthread_cancel()取消而不调用任何取消点的函数......但是这可以通过使用pthread_setcanceltype()将线程的取消类型设置为异步而不是推迟。为此,您需要添加类似pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL)的内容;在开始循环之前,在线程函数的开头附近。然后,您可以通过从main()调用pthread_cancel(th)来终止线程。

Note, though, that cancelling threads this way (whether asynchronous or not) doesn't clean up any resources allocated in the thread function (as noted by Kevin in a comment). In order to do this cleanly, you can:

但是请注意,以这种方式取消线程(无论是否异步)都不会清除线程函数中分配的任何资源(如Kevin在评论中所述)。为了干净利落,您可以:

  • Ensure that the thread doesn't do anything it needs to clean up before exit (e.g. using malloc() to allocate a buffer)
  • 确保线程在退出之前没有做任何需要清理的事情(例如使用malloc()来分配缓冲区)
  • Ensure that you have some way of cleaning up after the thread elsewhere, after the thread exits
  • 在线程退出后,确保在线程之后有一些清理方法
  • Use pthread_cleanup_push() and pthread_cleanup_pop() to add cleanup handlers to clean up resources when the thread is cancelled. Note that this is still risky if the cancellation type is asynchronous, because the thread could be cancelled between allocating a resource and adding the cleanup handler.
  • 使用pthread_cleanup_push()和pthread_cleanup_pop()添加清理处理程序以在取消线程时清理资源。请注意,如果取消类型是异步的,这仍然存在风险,因为可以在分配资源和添加清理处理程序之间取消线程。
  • Avoid using pthread_cancel() and have the thread check some condition to determine when to terminate (which would be checked in long-running loops). Since your thread then checks for termination itself, it can do whatever cleanup it needs to after the check.
  • 避免使用pthread_cancel()并让线程检查一些条件以确定何时终止(将在长时间运行的循环中检查)。由于您的线程然后检查终止本身,它可以在检查后执行它需要的任何清理。

One way of implementing the last option is to use a mutex as a flag, and test it with pthread_mutex_trylock() wrapped in a function to use in the loop tests:

实现最后一个选项的一种方法是使用互斥锁作为标志,并使用包含在函数中的pthread_mutex_trylock()来测试它以在循环测试中使用:

#include <pthread.h>
#include <unistd.h>
#include <errno.h>

/* Returns 1 (true) if the mutex is unlocked, which is the
 * thread's signal to terminate. 
 */
int needQuit(pthread_mutex_t *mtx)
{
  switch(pthread_mutex_trylock(mtx)) {
    case 0: /* if we got the lock, unlock and return 1 (true) */
      pthread_mutex_unlock(mtx);
      return 1;
    case EBUSY: /* return 0 (false) if the mutex was locked */
      return 0;
  }
  return 1;
}

/* Thread function, containing a loop that's infinite except that it checks for
 * termination with needQuit() 
 */
void *thread_do(void *arg)
{
  pthread_mutex_t *mx = arg;
  while( !needQuit(mx) ) {}
  return NULL;
}

int main(int argc, char *argv[])
{
  pthread_t th;
  pthread_mutex_t mxq; /* mutex used as quit flag */

  /* init and lock the mutex before creating the thread.  As long as the
     mutex stays locked, the thread should keep running.  A pointer to the
     mutex is passed as the argument to the thread function. */
  pthread_mutex_init(&mxq,NULL);
  pthread_mutex_lock(&mxq);
  pthread_create(&th,NULL,thread_do,&mxq);

  sleep(2);

  /* unlock mxq to tell the thread to terminate, then join the thread */
  pthread_mutex_unlock(&mxq); 
  pthread_join(th,NULL);

  sleep(2);
  return 0;
}

If the thread is not detached (it generally isn't by default), you should call pthread_join() after stopping the thread. If the thread is detached, you don't need to join it, but you won't know exactly when it terminates (or even approximately, unless you add another way to indicate its exit).

如果线程未分离(通常不是默认情况下),则应在停止线程后调用pthread_join()。如果线程已分离,则您不需要加入它,但您不会确切地知道它何时终止(或者甚至是大约,除非您添加另一种方式来指示其退出)。

#2


5  

A few small thoughts:

一些小小的想法:

  1. You're trying to cancel your thread, but if the cancellation policy in place is for a deferred cancellation, your thread_do() will never be canceled, because it never calls any functions that are cancellation points:

    您正在尝试取消您的线程,但如果适当的取消策略是延迟取消,您的thread_do()将永远不会被取消,因为它永远不会调用任何取消点的函数:

    A thread's cancellation type, determined by
    pthread_setcanceltype(3), may be either asynchronous or
    deferred (the default for new threads).  Asynchronous
    cancelability means that the thread can be canceled at any
    time (usually immediately, but the system does not guarantee
    this).  Deferred cancelability means that cancellation will
    be delayed until the thread next calls a function that is a
    cancellation point.  A list of functions that are or may be
    cancellation points is provided in pthreads(7).
    
  2. You're not joining the thread in your simple example code; call pthread_join(3) before the end of your program:

    您没有加入简单示例代码中的线程;在程序结束前调用pthread_join(3):

    After a canceled thread has terminated, a join with that
    thread using pthread_join(3) obtains PTHREAD_CANCELED as the
    thread's exit status.  (Joining with a thread is the only way
    to know that cancellation has completed.)
    

#1


34  

As @sarnold pointed out, by default your thread can't be cancelled with pthread_cancel() without calling any functions that are cancellation points... but this can be changed by using pthread_setcanceltype() to set the thread's cancellation type to asynchronous instead of deferred. To do that, you'd add something like pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL); near the start of your thread function, before you start the loop. You would then be able to terminate the thread by calling pthread_cancel(th) from main().

正如@sarnold指出的那样,默认情况下你的线程不能用pthread_cancel()取消而不调用任何取消点的函数......但是这可以通过使用pthread_setcanceltype()将线程的取消类型设置为异步而不是推迟。为此,您需要添加类似pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS,NULL)的内容;在开始循环之前,在线程函数的开头附近。然后,您可以通过从main()调用pthread_cancel(th)来终止线程。

Note, though, that cancelling threads this way (whether asynchronous or not) doesn't clean up any resources allocated in the thread function (as noted by Kevin in a comment). In order to do this cleanly, you can:

但是请注意,以这种方式取消线程(无论是否异步)都不会清除线程函数中分配的任何资源(如Kevin在评论中所述)。为了干净利落,您可以:

  • Ensure that the thread doesn't do anything it needs to clean up before exit (e.g. using malloc() to allocate a buffer)
  • 确保线程在退出之前没有做任何需要清理的事情(例如使用malloc()来分配缓冲区)
  • Ensure that you have some way of cleaning up after the thread elsewhere, after the thread exits
  • 在线程退出后,确保在线程之后有一些清理方法
  • Use pthread_cleanup_push() and pthread_cleanup_pop() to add cleanup handlers to clean up resources when the thread is cancelled. Note that this is still risky if the cancellation type is asynchronous, because the thread could be cancelled between allocating a resource and adding the cleanup handler.
  • 使用pthread_cleanup_push()和pthread_cleanup_pop()添加清理处理程序以在取消线程时清理资源。请注意,如果取消类型是异步的,这仍然存在风险,因为可以在分配资源和添加清理处理程序之间取消线程。
  • Avoid using pthread_cancel() and have the thread check some condition to determine when to terminate (which would be checked in long-running loops). Since your thread then checks for termination itself, it can do whatever cleanup it needs to after the check.
  • 避免使用pthread_cancel()并让线程检查一些条件以确定何时终止(将在长时间运行的循环中检查)。由于您的线程然后检查终止本身,它可以在检查后执行它需要的任何清理。

One way of implementing the last option is to use a mutex as a flag, and test it with pthread_mutex_trylock() wrapped in a function to use in the loop tests:

实现最后一个选项的一种方法是使用互斥锁作为标志,并使用包含在函数中的pthread_mutex_trylock()来测试它以在循环测试中使用:

#include <pthread.h>
#include <unistd.h>
#include <errno.h>

/* Returns 1 (true) if the mutex is unlocked, which is the
 * thread's signal to terminate. 
 */
int needQuit(pthread_mutex_t *mtx)
{
  switch(pthread_mutex_trylock(mtx)) {
    case 0: /* if we got the lock, unlock and return 1 (true) */
      pthread_mutex_unlock(mtx);
      return 1;
    case EBUSY: /* return 0 (false) if the mutex was locked */
      return 0;
  }
  return 1;
}

/* Thread function, containing a loop that's infinite except that it checks for
 * termination with needQuit() 
 */
void *thread_do(void *arg)
{
  pthread_mutex_t *mx = arg;
  while( !needQuit(mx) ) {}
  return NULL;
}

int main(int argc, char *argv[])
{
  pthread_t th;
  pthread_mutex_t mxq; /* mutex used as quit flag */

  /* init and lock the mutex before creating the thread.  As long as the
     mutex stays locked, the thread should keep running.  A pointer to the
     mutex is passed as the argument to the thread function. */
  pthread_mutex_init(&mxq,NULL);
  pthread_mutex_lock(&mxq);
  pthread_create(&th,NULL,thread_do,&mxq);

  sleep(2);

  /* unlock mxq to tell the thread to terminate, then join the thread */
  pthread_mutex_unlock(&mxq); 
  pthread_join(th,NULL);

  sleep(2);
  return 0;
}

If the thread is not detached (it generally isn't by default), you should call pthread_join() after stopping the thread. If the thread is detached, you don't need to join it, but you won't know exactly when it terminates (or even approximately, unless you add another way to indicate its exit).

如果线程未分离(通常不是默认情况下),则应在停止线程后调用pthread_join()。如果线程已分离,则您不需要加入它,但您不会确切地知道它何时终止(或者甚至是大约,除非您添加另一种方式来指示其退出)。

#2


5  

A few small thoughts:

一些小小的想法:

  1. You're trying to cancel your thread, but if the cancellation policy in place is for a deferred cancellation, your thread_do() will never be canceled, because it never calls any functions that are cancellation points:

    您正在尝试取消您的线程,但如果适当的取消策略是延迟取消,您的thread_do()将永远不会被取消,因为它永远不会调用任何取消点的函数:

    A thread's cancellation type, determined by
    pthread_setcanceltype(3), may be either asynchronous or
    deferred (the default for new threads).  Asynchronous
    cancelability means that the thread can be canceled at any
    time (usually immediately, but the system does not guarantee
    this).  Deferred cancelability means that cancellation will
    be delayed until the thread next calls a function that is a
    cancellation point.  A list of functions that are or may be
    cancellation points is provided in pthreads(7).
    
  2. You're not joining the thread in your simple example code; call pthread_join(3) before the end of your program:

    您没有加入简单示例代码中的线程;在程序结束前调用pthread_join(3):

    After a canceled thread has terminated, a join with that
    thread using pthread_join(3) obtains PTHREAD_CANCELED as the
    thread's exit status.  (Joining with a thread is the only way
    to know that cancellation has completed.)