全部博文(73)
分类: LINUX
2009-04-23 16:27:08
pthread之线程取消
第二春刚来的时候,和我讨论过一个关于程序多线程的结构的问题,打算采用pthread_cancel来处理,为了满足下面的要求,于是有了下面的代码:) 前两天再和春讨论,我发现春懂的比我懂的多多了:(
线程取消并不常用,通常做为多线程编程中的高级话题,更为详细的描述请看<
我们的需求:
1. 利用pthread cancel的属性,保证某一段代码完整的执行(面对pthread_cancel互斥量不能做到这一点
2. 线程send发送信号给digest让其运行,但如果digest不在运行状态,则该信号丢失 线程send在收到cancel之后不保证事件处理,直接退出
3. 线程send在收到cancel之后不保证事件处理,直接退出
4. 当digest线程不再等待而在执行过程时(比如sleep)时,sender发送的信号被丢失
5. sender发送5个信号给digest,可能digest最终只能消化3个信号,它只保证这三个信号被完整执行,而不中断,但不保存所有的信号都被执行。可能通过输出看到digest在取消信号发出之后依然执行完成最后的一次处理。
6. 如果要保证所有发出的信号都被执行,有两种方式:
首先来看看整个目录的结构:
dongq@DongQ:~/test$ tree thread_cancel
thread_cancel
|-- Makefile -make
|-- main.c --主线程
|-- main.h --数据结构
`-- thread.c --线程体
0 directories, 4 files
我们一个个来看,先看看main.h,在它里面只定义了一个数据结构和两个全变量:
#ifndef MAIN_H
#define MAIN_H
#include
#include
#include
#include
#include
#include
#include
pthread_mutex_t mutex;
pthread_cond_t cond;
typedef struct test
{
int a; //digest
int b; //sender线程休息时间间隔
}Test;
#endif
再来看看main.c中有什么东西,在主线程中,创建一个 send线程,一个digest线程,相当于send告诉digest去做什么,digest就
尽自己最大的努力去做此事。
#include "main.h"
extern void * sendData( void * arg );
extern void * digestData( void * arg );
void my_exit();
pthread_t send_pid;
pthread_t digest_pid;
int main()
{
Test x = { 2, 33 };
printf(
"PTHREAD_CANCEL_ENABLE:%d PTHREAD_CANCEL_DISABLE:%d\n",
PTHREAD_CANCEL_ENABLE,
PTHREAD_CANCEL_DISABLE);
pthread_mutex_init( &mutex,
NULL );
pthread_cond_init( &cond, NULL
);
//创建digest线程
if ( pthread_create(
&digest_pid, 0, digestData, &x ) )
{
printf( "Create
digestData thread error\n" );
exit( -1 );
}
//创建sender线程
if ( pthread_create(
&send_pid, 0, sendData, &x ) )
{
printf( "Create
sendData thread error\n" );
exit( -1 );
}
//等待一段时间
sleep( x.b / 3 );
my_exit();
return 0;
}
void my_exit()
{
printf(" cancel
sendpid\n");
//通知send线程通出,send则立即退出
pthread_cancel( send_pid );
printf(" cancel
disgestpid\n");
//通知digest线程退出
, digest将在执行完当前的任务之后再退出
pthread_cancel( digest_pid );
printf(" join
sendpid\n");
pthread_join( send_pid, 0 );
printf(" join
disgestpid\n");
pthread_join( digest_pid, 0 );
printf( "parent exit.\n"
);
}
现在来看看子线程体,是如何来满足我们的需求的:
#include "main.h"
void * digestData( void * arg )
{
int status;
Test * x = ( Test * )arg ;
while( 1 )
{
pthread_mutex_lock(
&mutex );
status = pthread_cond_wait(
&cond, &mutex );
pthread_mutex_unlock(
&mutex );
if ( status != 0 )
{
printf(
"condition wait failure!!!\n" );
}
int c = x->a;
printf( "digest sleep
time: %d\n", c );
//设置线程,禁用线程取消功能
pthread_setcancelstate(
PTHREAD_CANCEL_DISABLE, &status );
if ( status !=
PTHREAD_CANCEL_ENABLE )
{
printf(" !!set
cancel state failure!!!\n");
}
while( c-- ) //线程休眠,取消有机会在这个时间段发生,但取消只被记下,不马上退出,保证当前任务执行完成
{
printf(
"digest\n" ); //虽然是个取消点,但是取消被diable了,这样系统将会有一个未被解决的取消
sleep ( 1 );
}
printf( "digest the
data from sender!!!\n" );
//启用取消功能,默认取消延迟处理取消请求
pthread_setcancelstate(
PTHREAD_CANCEL_ENABLE, &status );
if ( status !=
PTHREAD_CANCEL_DISABLE )
{
printf("**set
cancel state failure!!!%d\n", status);
}
}
return (void *)0;
}
//当send在运行时,得到取消请求后,将在下一个取消点退出
void * sendData( void * arg )
{
Test * x = ( Test * )arg ;
int c = x->b;
printf( "sender sleep
time:%d\n", c );
while( c-- )
{
printf( "send\n"
);
sleep( 1 );
//每隔几秒才发送信号
if ( c % 3 )
{
pthread_mutex_lock(
&mutex );
printf( "send
command to digest data\n" );
pthread_cond_signal(
&cond );
pthread_mutex_unlock(
&mutex );
}
}
return (void *)0;
}
在清除处理函数中等待不是一个好主意(因为毕竟该线程是用来清除的,而不是用来等待某件事的发生)。
但是如果你的清除处理函数确实需要等待一些东西,它也会工作得很好。