threads

一:

pthread_detach()即主线程与子线程分离,两者相互不干涉,子线程结束同时子线程的资源自动回收。pthread_join()即是子线程合入主线程,主线程会一直阻塞,直到子线程执行结束,然后回收子线程资源,并继续执行。

二:

pthread_cleanup_push / pthread_cleanup_pop

三:

pthread_cond_signal和pthread_cond_wait简介

pthread_cond_wait() 用于阻塞当前线程,等待别的线程使用pthread_cond_signal()或pthread_cond_broadcast来唤醒它。 

 

pthread_cond_wait() 必须与pthread_mutex 配套使用。

pthread_cond_wait()函数一进入wait状态,就会自动release mutex。当其他线程通过pthread_cond_signal()或pthread_cond_broadcast,把该线程唤醒,使pthread_cond_wait()通过(返回)时,该线程又自动获得该mutex。

pthread_cond_signal函数的作用是发送一个信号给另外一个正在处于阻塞等待状态的线程,使其脱离阻塞状态,继续执行。如果没有线程处在阻塞等待状态,pthread_cond_signal也会成功返回。使用pthread_cond_signal一般不会有“惊群现象”产生,它最多只给一个线程发信号。假如有多个线程正在阻塞等待着这个条件变量的话,那么是根据各等待线程优先级的高低,确定哪个线程接收到信号开始继续执行。如果各线程优先级相同,则根据等待时间的长短来确定哪个线程获得信号。

无论如何,一个pthread_cond_signal调用最多发信一次。但是pthread_cond_signal在多处理器上可能同时唤醒多个线程,当你只能让一个线程处理某个任务时,其它被唤醒的线程就需要继续 wait,而且规范要求pthread_cond_signal至少唤醒一个pthread_cond_wait上的线程,其实有些实现为了简单在单处理器上也会唤醒多个线程。 另外,某些应用,如线程池,pthread_cond_broadcast唤醒全部线程,但我们通常只需要一部分线程去做执行任务,所以其它的线程需要继续wait。所以强烈推荐对pthread_cond_wait() 使用while循环来做条件判断。

以下就是一个来自MAN的示例

  Consider two shared variables x and y, protected by the mutex mut, and a condition variable cond that is to be signaled whenever x becomes greater than y.

 

int x,y;
pthread_mutex_t mut = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

 

 Waiting until x is greater than y is performed as follows:

pthread_mutex_lock(&mut);
while (x <= y) 
{
pthread_cond_wait(&cond, &mut);
}
/* operate on x and y */
pthread_mutex_unlock(&mut);

 

 Modifications on x and y that may cause x to become greater than y should signal the condition if needed:

pthread_mutex_lock(&mut);
/* modify x and y */
if (x > y) pthread_cond_broadcast(&cond);
pthread_mutex_unlock(&mut);

 

pthread_cond_signal函数与条件变量的典型应用就是用来实现producer/consumer模型。

示例1

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


#define BUFFER_SIZE 8

struct Products
{
int buffer[BUFFER_SIZE];
/*保证存取操作的原子性 互斥性*/
pthread_mutex_t locker;
/*是否可读*/           
pthread_cond_t notEmpty;
/*是否可写*/  
pthread_cond_t notFull;
int posReadFrom;
int posWriteTo;
};


int BufferIsFull(struct Products* products)
{
if ((products->posWriteTo + 1) % BUFFER_SIZE == products->posReadFrom)
{
return (1);
}
return (0);
}


int BufferIsEmpty(struct Products* products)
{
if (products->posWriteTo == products->posReadFrom)
{
return (1);
}

return (0);
}

/*制造产品*/
void Produce(struct Products* products, int item)
{
/*原子操作*/
pthread_mutex_lock(&products->locker);
/*无空间可写入*/
while (BufferIsFull(products))
{
pthread_cond_wait(&products->notFull, &products->locker);


/*写入数据*/
products->buffer[products->posWriteTo] = item;
products->posWriteTo++;

if (products->posWriteTo >= BUFFER_SIZE)
products->posWriteTo = 0;
/*发信*/
pthread_cond_signal(&products->notEmpty);
/*解锁*/
pthread_mutex_unlock(&products->locker);
}

int Consume(struct Products* products)
{
int item;

pthread_mutex_lock(&products->locker);
/*为空时持续等待,无数据可读*/
while (BufferIsEmpty(products))
{
pthread_cond_wait(&products->notEmpty, &products->locker);
}

/*提取数据*/
item = products->buffer[products->posReadFrom];
products->posReadFrom++;
/*如果到末尾,从头读取*/
if (products->posReadFrom >= BUFFER_SIZE)
products->posReadFrom = 0;


pthread_cond_signal(&products->notFull); 
pthread_mutex_unlock(&products->locker);


return item;
}


#define END_FLAG (-1)

struct Products products;

void* ProducerThread(void* data)
{
int i;
for (i = 0; i < 16; ++i)
{
printf("producer: %d\n", i);
Produce(&products, i);
}
Produce(&products, END_FLAG);
return NULL;
}

void* ConsumerThread(void* data)
{
int item;

while (1)
{
item = Consume(&products);
if (END_FLAG == item)
break;
printf("consumer: %d\n", item);
}
return (NULL);
}

int main(int argc, char* argv[])
{
pthread_t producer;
pthread_t consumer;
int result;

pthread_create(&producer, NULL, &ProducerThread, NULL);
pthread_create(&consumer, NULL, &ConsumerThread, NULL);

pthread_join(producer, (void *)&result);
pthread_join(consumer, (void *)&result);

exit(EXIT_SUCCESS);
}

 

示例2

pthread_cond_broadcast的使用

[cpp] view plaincopy

  1. #include <pthread.h>  

  2. #include <stdlib.h>  

  3. #include <stdio.h>  

  4. #include <unistd.h>  

  5. #include <string.h>  

  6. pthread_mutex_t mymutex1 = PTHREAD_MUTEX_INITIALIZER;  

  7. pthread_mutex_t mymutex2 = PTHREAD_MUTEX_INITIALIZER;  

  8. pthread_cond_t mycond = PTHREAD_COND_INITIALIZER;  

  9. void *mythread1(void *param)  

  10. {  

  11.     printf("begin mythread1.\n");  

  12.     pthread_mutex_lock(&mymutex1);  

  13.     printf("wait in mythread1.\n");  

  14.     pthread_cond_wait(&mycond,&mymutex1);  

  15.     pthread_mutex_unlock(&mymutex1);  

  16.     printf("end mythread1.\n");  

  17.     return NULL;  

  18. }  

  19. void *mythread2(void *param)  

  20. {  

  21.     printf("begin mythread2.\n");  

  22.     pthread_mutex_lock(&mymutex2);  

  23.     printf("wait in mythread2.\n");  

  24.     pthread_cond_wait(&mycond,&mymutex2);  

  25.     pthread_mutex_unlock(&mymutex2);  

  26.     printf("end mythread2.\n");  

  27.     return NULL;  

  28. }  

  29. int main(void)  

  30. {  

  31.     printf("begin main thread.\n");  

  32.     int i;  

  33.     pthread_t tid1,tid2;  

  34.     pthread_create(&tid1,NULL,mythread1,NULL);  

  35.     pthread_create(&tid2,NULL,mythread2,NULL);  

  36.     sleep(2);  

  37.     printf("try to wake up mythread1 and mythread2 in main thread.\n");  

  38.     if(pthread_cond_broadcast(&mycond)){  

  39.         printf("error\n");  

  40.         return 1;  

  41.     }  

  42.     void *res;  

  43.     pthread_join(tid1, &res);  

  44.     pthread_join(tid2, &res);  

  45.     printf("end main thread.\n");  

  46.     return 0;  

  47. }  

运行结果:

begin main thread.
begin mythread1.
wait in mythread1.
begin mythread2.
wait in mythread2.
end mythread2.
try to wake up mythread1 and mythread2 in main thread.
end mythread1.
end main thread.

注意mythread2并未真正的等待,它和我们的期望有所差别,似乎一个pthread_cond_t只能对应一个pthread_mutex_t。

我们把以上代码稍作修正就可以了,具体见下面的示例3。

示例3

[cpp] view plaincopy

  1. #include <pthread.h>  

  2. #include <stdlib.h>  

  3. #include <stdio.h>  

  4. #include <unistd.h>  

  5. #include <string.h>  

  6. pthread_mutex_t mymutex1 = PTHREAD_MUTEX_INITIALIZER;  

  7. //pthread_mutex_t mymutex2 = PTHREAD_MUTEX_INITIALIZER;  

  8. pthread_cond_t mycond = PTHREAD_COND_INITIALIZER;  

  9. void *mythread1(void *param)  

  10. {  

  11.     printf("begin mythread1.\n");  

  12.     pthread_mutex_lock(&mymutex1);  

  13.     printf("wait in mythread1.\n");  

  14.     pthread_cond_wait(&mycond,&mymutex1);  

  15.     pthread_mutex_unlock(&mymutex1);  

  16.     printf("end mythread1.\n");  

  17.     return NULL;  

  18. }  

  19. void *mythread2(void *param)  

  20. {  

  21.     printf("begin mythread2.\n");  

  22.     pthread_mutex_lock(&mymutex1);  

  23.     printf("wait in mythread2.\n");  

  24.     pthread_cond_wait(&mycond,&mymutex1);  

  25.     pthread_mutex_unlock(&mymutex1);  

  26.     printf("end mythread2.\n");  

  27.     return NULL;  

  28. }  

  29. int main(void)  

  30. {  

  31.     printf("begin main thread.\n");  

  32.     int i;  

  33.     pthread_t tid1,tid2;  

  34.     pthread_create(&tid1,NULL,mythread1,NULL);  

  35.     pthread_create(&tid2,NULL,mythread2,NULL);  

  36.     sleep(2);  

  37.     printf("try to wake up mythread1 and mythread2 in main thread.\n");  

  38.     if(pthread_cond_broadcast(&mycond)){  

  39.         printf("error\n");  

  40.         return 1;  

  41.     }  

  42.     void *res;  

  43.     pthread_join(tid1, &res);  

  44.     pthread_join(tid2, &res);  

  45.     printf("end main thread.\n");  

  46.     return 0;  

  47. }  

运行结果

123@xyy ~/gcc-test
$ gcc threadTest.c -o test.exe
123@xyy ~/gcc-test
$ ./test.exe
begin main thread.
begin mythread1.
wait in mythread1.
begin mythread2.
wait in mythread2.
try to wake up mythread1 and mythread2 in main thread.
end mythread1.
end mythread1.
end main thread.

该结果才是我们真正需要的。

结束!

 

原文地址:

http://blog.csdn.net/hudashi/article/details/7709421

 

四:

pthread_cleanup_push(3) - Linux man page

Name

pthread_cleanup_push, pthread_cleanup_pop - push and pop thread cancellation clean-up handlers

Synopsis

#include <pthread.h>

void pthread_cleanup_push(void (*routine)(void *),
                          void *
arg);
void pthread_cleanup_pop(int
execute);

Compile and link with -pthread.

Description

 

These functions manipulate the calling thread's stack of thread-cancellation clean-up handlers. A clean-up handler is a function that is automatically executed when a thread is canceled (or in various other circumstances described below); it might, for example, unlock a mutex so that it becomes available to other threads in the process.

The pthread_cleanup_push() function pushes routine onto the top of the stack of clean-up handlers. When routine is later invoked, it will be given arg as its argument.

The pthread_cleanup_pop() function removes the routine at the top of the stack of clean-up handlers, and optionally executes it if execute is nonzero.

A cancellation clean-up handler is popped from the stack and executed in the following circumstances:

1.

When a thread is canceled, all of the stacked clean-up handlers are popped and executed in the reverse of the order in which they were pushed onto the stack.

2.

When a thread terminates by calling pthread_exit(3), all clean-up handlers are executed as described in the preceding point. (Clean-up handlers are not called if the thread terminates by performing a return from the thread start function.)

3.

When a thread calls pthread_cleanup_pop() with a nonzero execute argument, the top-most clean-up handler is popped and executed.

POSIX.1 permits pthread_cleanup_push() and pthread_cleanup_pop() to be implemented as macros that expand to text containing '{' and '}', respectively. For this reason, the caller must ensure that calls to these functions are paired within the same function, and at the same lexical nesting level. (In other words, a clean-up handler is only established during the execution of a specified section of code.)

Calling longjmp(3) (siglongjmp(3)) produces undefined results if any call has been made to pthread_cleanup_push() or pthread_cleanup_pop() without the matching call of the pair since the jump buffer was filled by setjmp(3) (sigsetjmp(3)). Likewise, calling longjmp(3) (siglongjmp(3)) from inside a clean-up handler produces undefined results unless the jump buffer was also filled by setjmp(3) (sigsetjmp(3)) inside the handler.

Return Value

These functions do not return a value.

Errors

There are no errors.

Conforming To

POSIX.1-2001.

Notes

On Linux, the pthread_cleanup_push() and pthread_cleanup_pop() functions are implemented as macros that expand to text containing '{' and '}', respectively. This means that variables declared within the scope of paired calls to these functions will only be visible within that scope.

POSIX.1 says that the effect of using return, break, continue, or goto to prematurely leave a block bracketed pthread_cleanup_push() and pthread_cleanup_pop() is undefined. Portable applications should avoid doing this.

Example

The program below provides a simple example of the use of the functions described in this page. The program creates a thread that executes a loop bracketed by pthread_cleanup_push() and pthread_cleanup_pop(). This loop increments a global variable, cnt, once each second. Depending on what command-line arguments are supplied, the main thread sends the other thread a cancellation request, or sets a global variable that causes the other thread to exit its loop and terminate normally (by doing a return).

In the following shell session, the main thread sends a cancellation request to the other thread:

$ ./a.out
New thread started
cnt = 0
cnt = 1
Canceling thread
Called clean-up handler
Thread was canceled; cnt = 0

From the above, we see that the thread was canceled, and that the cancellation clean-up handler was called and it reset the value of the global variable cnt to 0.

In the next run, the main program sets a global variable that causes other thread to terminate normally:

$ ./a.out x
New thread started
cnt = 0
cnt = 1
Thread terminated normally; cnt = 2

From the above, we see that the clean-up handler was not executed (because cleanup_pop_arg was 0), and therefore the value of cnt was not reset.

In the next run, the main program sets a global variable that causes the other thread to terminate normally, and supplies a nonzero value for cleanup_pop_arg:

$ ./a.out x 1
New thread started
cnt = 0
cnt = 1
Called clean-up handler
Thread terminated normally; cnt = 0

In the above, we see that although the thread was not canceled, the clean-up handler was executed, because the argument given to pthread_cleanup_pop() was nonzero.

Program source

#include <pthread.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>

#define handle_error_en(en, msg) \
        do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

static int done = 0;
static int cleanup_pop_arg = 0;
static int cnt = 0;

static void
cleanup_handler(void *arg)
{
    printf("Called clean-up handler\n");
    cnt = 0;
}

static void *
thread_start(void *arg)
{
    time_t start, curr;

   printf("New thread started\n");

   pthread_cleanup_push(cleanup_handler, NULL);

   curr = start = time(NULL);

   while (!done) {
        pthread_testcancel();           /* A cancellation point */
        if (curr < time(NULL)) {
            curr = time(NULL);
            printf("cnt = %d\n", cnt);  /* A cancellation point */
            cnt++;
        }
    }

   pthread_cleanup_pop(cleanup_pop_arg);
    return NULL;
}

int
main(int argc, char *argv[])
{
    pthread_t thr;
    int s;
    void *res;

   s = pthread_create(&thr, NULL, thread_start, NULL);
    if (s != 0)
        handle_error_en(s, "pthread_create");

   sleep(2);           /* Allow new thread to run a while */

   if (argc > 1) {
        if (argc > 2)
            cleanup_pop_arg = atoi(argv[2]);
        done = 1;

   } else {
        printf("Canceling thread\n");
        s = pthread_cancel(thr);
        if (s != 0)
            handle_error_en(s, "pthread_cancel");
    }

   s = pthread_join(thr, &res);
    if (s != 0)
        handle_error_en(s, "pthread_join");

   if (res == PTHREAD_CANCELED)
        printf("Thread was canceled; cnt = %d\n", cnt);
    else
        printf("Thread terminated normally; cnt = %d\n", cnt);
    exit(EXIT_SUCCESS);
}

See Also

pthread_cancel(3), pthread_cleanup_push_defer_np(3), pthread_setcancelstate(3), pthread_testcancel(3), pthreads(7)

Referenced By

pthread_setcanceltype(3)

 

五:

线程取消(pthread_cancel)

 

基本概念

pthread_cancel调用并不等待线程终止,它只提出请求。线程在取消请求(pthread_cancel)发出后会继续运行,

直到到达某个取消点(CancellationPoint)。取消点是线程检查是否被取消并按照请求进行动作的一个位置.

与线程取消相关的pthread函数

int pthread_cancel(pthread_t thread)

发送终止信号给thread线程,如果成功则返回0,否则为非0值。发送成功并不意味着thread会终止。

int pthread_setcancelstate(int state,   int *oldstate)

设置本线程对Cancel信号的反应,state有两种值:PTHREAD_CANCEL_ENABLE(缺省)和PTHREAD_CANCEL_DISABLE,

分别表示收到信号后设为CANCLED状态和忽略CANCEL信号继续运行;old_state如果不为NULL则存入原来的Cancel状态以便恢复。

int pthread_setcanceltype(int type, int *oldtype)

设置本线程取消动作的执行时机,type由两种取值:PTHREAD_CANCEL_DEFFERED和PTHREAD_CANCEL_ASYCHRONOUS,仅当Cancel状态为Enable时有效,分别表示收到信号后继续运行至下一个取消点再退出和立即执行取消动作(退出);oldtype如果不为NULL则存入运来的取消动作类型值。

void pthread_testcancel(void)

是说pthread_testcancel在不包含取消点,但是又需要取消点的地方创建一个取消点,以便在一个没有包含取消点的执行代码线程中响应取消请求.

线程取消功能处于启用状态且取消状态设置为延迟状态时,pthread_testcancel()函数有效。

如果在取消功能处处于禁用状态下调用pthread_testcancel(),则该函数不起作用。

请务必仅在线程取消线程操作安全的序列中插入pthread_testcancel()。除通过pthread_testcancel()调用以编程方式建立的取消点意外,pthread标准还指定了几个取消点。测试退出点,就是测试cancel信号.

取消点:

线程取消的方法是向目标线程发Cancel信号,但如何处理Cancel信号则由目标线程自己决定,或者忽略、或者立即终止、或者继续运行至Cancelation-point(取消点),由不同的Cancelation状态决定。

线程接收到CANCEL信号的缺省处理(即pthread_create()创建线程的缺省状态)是继续运行至取消点,也就是说设置一个CANCELED状态,线程继续运行,只有运行至Cancelation-point的时候才会退出。

pthreads标准指定了几个取消点,其中包括:

(1)通过pthread_testcancel调用以编程方式建立线程取消点。

(2)线程等待pthread_cond_wait或pthread_cond_timewait()中的特定条件。

(3)被sigwait(2)阻塞的函数

(4)一些标准的库调用。通常,这些调用包括线程可基于阻塞的函数。

缺省情况下,将启用取消功能。有时,您可能希望应用程序禁用取消功能。如果禁用取消功能,则会导致延迟所有的取消请求,

直到再次启用取消请求。

根据POSIX标准,pthread_join()、pthread_testcancel()、pthread_cond_wait()、pthread_cond_timedwait()、sem_wait()、sigwait()等函数以及

read()、write()等会引起阻塞的系统调用都是Cancelation-point,而其他pthread函数都不会引起Cancelation动作。

但是pthread_cancel的手册页声称,由于LinuxThread库与C库结合得不好,因而目前C库函数都不是Cancelation-point;但CANCEL信号会使线程从阻塞的系统调用中退出,并置EINTR错误码,因此可以在需要作为Cancelation-point的系统调用前后调用pthread_testcancel(),从而达到POSIX标准所要求的目标.

即如下代码段:

pthread_testcancel();

retcode = read(fd, buffer, length);

pthread_testcancel();

注意:

程序设计方面的考虑,如果线程处于无限循环中,且循环体内没有执行至取消点的必然路径,则线程无法由外部其他线程的取消请求而终止。因此在这样的循环体的必经路径上应该加入pthread_testcancel()调用.

取消类型(Cancellation Type)

我们会发现,通常的说法:某某函数是 Cancellation Points,这种方法是容易令人混淆的。

因为函数的执行是一个时间过程,而不是一个时间点。其实真正的 Cancellation Points 只是在这些函数中 Cancellation Type 被修改为 PHREAD_CANCEL_ASYNCHRONOUS 和修改回 PTHREAD_CANCEL_DEFERRED 中间的一段时间。

POSIX的取消类型有两种,一种是延迟取消(PTHREAD_CANCEL_DEFERRED),这是系统默认的取消类型,即在线程到达取消点之前,不会出现真正的取消;另外一种是异步取消(PHREAD_CANCEL_ASYNCHRONOUS),使用异步取消时,线程可以在任意时间取消。

线程终止的清理工作

Posix的线程终止有两种情况:正常终止和非正常终止。

线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;

非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。

不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。

最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

在POSIX线程API中提供了一个pthread_cleanup_push()/ pthread_cleanup_pop()函数,

对用于自动释放资源—从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。

API定义如下:

void pthread_cleanup_push(void (*routine) (void *), void *arg)

void pthread_cleanup_pop(int execute)

pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数

在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push() 的调用将在清理函数栈中形成一个函数链;

从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()和异常终止,不包括return)

都将执行pthread_cleanup_push()所指定的清理函数。

在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到 pthread_cleanup_pop()时

是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。

pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

 

#define pthread_cleanup_push(routine,arg) \

{

struct _pthread_cleanup_buffer _buffer; \

_pthread_cleanup_push (&_buffer, (routine), (arg));

#define pthread_cleanup_pop(execute) \

_pthread_cleanup_pop (&_buffer, (execute)); \

}

 

可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。

在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。

 

pthread_cleanup_push(pthread_mutex_unlock, (void*) &mut);

pthread_mutex_lock(&mut);

/* do some work */

pthread_mutex_unlock(&mut);

pthread_cleanup_pop(0);

或者

void cleanup(void *arg)

{

pthread_mutex_unlock(&mutex);

}

void* thread0(void* arg)

{

pthread_cleanup_push(cleanup, NULL); // thread cleanup handler    p

thread_mutex_lock(&mutex);

pthread_cond_wait(&cond, &mutex);

pthread_mutex_unlock(&mutex);

pthread_cleanup_pop(0);

pthread_exit(NULL);

}

 

六:

linux下pthread_cancel无法取消线程的原因

 

一个线程可以调用pthread_cancel终止同一进程中的另一个线程,但是值得强调的是:同一进程的线程间,pthread_cancel向另一线程发终止信号。系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,才会真正结束线程。或调用pthread_testcancel,让内核去检测是否需要取消当前线程。被取消的线程,退出值,定义在Linux的pthread库中常数PTHREAD_CANCELED的值是-1。

 

 

  1. #include <pthread.h>

  2.  

  3. int pthread_cancel(pthread_t thread);


看下面程序:

 

 

  1. #include<stdio.h>

  2. #include<stdlib.h>

  3. #include <pthread.h>

  4. void *thread_fun(void *arg)

  5. {

  6. int i=1;

  7. printf("thread start \n");

  8. while(1)

  9. {

  10. i++;

  11. }

  12. return (void *)0;

  13. }

  14. int main()

  15. {

  16. void *ret=NULL;

  17. int iret=0;

  18. pthread_t tid;

  19. pthread_create(&tid,NULL,thread_fun,NULL);

  20. sleep(1);

  21.  

  22. pthread_cancel(tid);//取消线程

  23. pthread_join(tid, &ret);

  24. printf("thread 3 exit code %d\n", (int)ret);

  25.  

  26. return 0;

  27.  

  28. }

 

会发现程序再一直运行,线程无法被取消,究其原因pthread_cancel向另一线程发终止信号。系统并不会马上关闭被取消线程,只有在被取消线程下次系统调用时,才会真正结束线程。如果线程里面没有执行系统调用,可以使用pthread_testcancel解决

 

 

  1. #include<stdio.h>

  2. #include<stdlib.h>

  3. #include <pthread.h>

  4. void *thread_fun(void *arg)

  5. {

  6. int i=1;

  7. printf("thread start \n");

  8. while(1)

  9. {

  10. i++;

  11. pthread_testcancel();

  12. }

  13. return (void *)0;

  14. }

  15. int main()

  16. {

  17. void *ret=NULL;

  18. int iret=0;

  19. pthread_t tid;

  20. pthread_create(&tid,NULL,thread_fun,NULL);

  21. sleep(1);

  22.  

  23. pthread_cancel(tid);//取消线程

  24. pthread_join(tid, &ret);

  25. printf("thread 3 exit code %d\n", (int)ret);

  26.  

  27. return 0;

  28.  

  29. }


七:

线程终止方式:pthread_cleanup_push/pthread_cleanup_pop()

 

    以下内容根据【1】进行整理。关于取消点,将在后面进一步讨论。

1、一般来说,Posix的线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit()或者从线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是线程在其他线程的干预下,或者由于自身运行出错(比如访问非法地址)而退出,这种退出方式是不可预见的。

2、线程终止时的清理

    不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,在不考虑因运行出错而退出的前提下,如何保证线程终止时能顺利的释放掉自己所占用的资源,特别是锁资源,就是一个必须考虑解决的问题。

    最经常出现的情形是资源独占锁的使用:线程为了访问临界资源而为其加上锁,但在访问过程中被外界取消,如果线程处于响应取消状态,且采用异步方式响应,或者在打开独占锁以前的运行路径上存在取消点,则该临界资源将永远处于锁定状态得不到释放。外界取消操作是不可预见的,因此的确需要一个机制来简化用于资源释放的编程。

    在POSIX线程API中提供了一个pthread_cleanup_push()/ pthread_cleanup_pop()函数对用于自动释放资源—从pthread_cleanup_push()的调用点到pthread_cleanup_pop()之间的程序段中的终止动作(包括调用pthread_exit()和取消点终止)都将执行pthread_cleanup_push()所指定的清理函数。

API定义如下:

void pthread_cleanup_push(void (*routine) (void *), void *arg)

void pthread_cleanup_pop(int execute)

    pthread_cleanup_push()/pthread_cleanup_pop()采用先入后出的栈结构管理,void routine(void *arg)函数在调用pthread_cleanup_push()时压入清理函数栈,多次对pthread_cleanup_push() 的调用将在清理函数栈中形成一个函数链;从pthread_cleanup_push的调用点到pthread_cleanup_pop之间的程序段中的终止动作(包括调用pthread_exit()和异常终止,不包括return)都将执行pthread_cleanup_push()所指定的清理函数。

 

        在执行该函数链时按照压栈的相反顺序弹出。execute参数表示执行到 pthread_cleanup_pop()时是否在弹出清理函数的同时执行该函数,为0表示不执行,非0为执行;这个参数并不影响异常终止时清理函数的执行。

    pthread_cleanup_push()/pthread_cleanup_pop()是以宏方式实现的,这是pthread.h中的宏定义:

#define pthread_cleanup_push(routine,arg) \

{

struct _pthread_cleanup_buffer _buffer; \

_pthread_cleanup_push (&_buffer, (routine), (arg));

 

#define pthread_cleanup_pop(execute) \

_pthread_cleanup_pop (&_buffer, (execute)); \

    }

    可见,pthread_cleanup_push()带有一个"{",而pthread_cleanup_pop()带有一个"}",因此这两个函数必须成对出现,且必须位于程序的同一级别的代码段中才能通过编译。

    在下面的例子里,当线程在"do some work"中终止时,将主动调用pthread_mutex_unlock(mut),以完成解锁动作。

pthread_cleanup_push(pthread_mutex_unlock, (void*) &mut);

pthread_mutex_lock(&mut);

/* do some work */

pthread_mutex_unlock(&mut);

pthread_cleanup_pop(0);

【1】 http://wenku.baidu.com/view/fd4a162e0066f5335a812191.html

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值