Chinaunix首页 | 论坛 | 博客
  • 博客访问: 192609
  • 博文数量: 73
  • 博客积分: 5000
  • 博客等级: 大校
  • 技术积分: 1160
  • 用 户 组: 普通用户
  • 注册时间: 2009-04-23 15:53
文章分类

全部博文(73)

文章存档

2011年(1)

2009年(72)

我的朋友

分类: LINUX

2009-04-23 16:27:08

pthread之线程取消

第二春刚来的时候,和我讨论过一个关于程序多线程的结构的问题,打算采用pthread_cancel来处理,为了满足下面的要求,于是有了下面的代码:)  前两天再和春讨论,我发现春懂的比我懂的多多了:(
      
线程取消并不常用,通常做为多线程编程中的高级话题,更为详细的描述请看<多线程程序设计>>一书,此书是多线程编程的大宝典呀!对各对概念做了详细的描述。此外,<环境高级编程>>涉及了大量posix中并未详述的内容。下面提个大概的内容。

  1. Phread_cancel是一种请求,而不是粗暴的命令。
  2. Phread允许线程控制自己的结束,它能恢复程序不变量并解锁互斥锁
  3. 支持推迟取消,取消只能发生在取消点:) 取消点的概念去看书吧,是个比较复杂的问题
  4. 取消状态有三种:分别由状态(disable,enable)与类型( deferred, asynchronous)组合而成,分别为off, deferred, async.
  5. 避免使用异步取消,这个东西很难用对,也用处不大:)
  6. 利用pthread_testcancel来构建自己的函数,使其能够在该函数中被取消

 我们的需求:
    1.
利用pthread cancel的属性,保证某一段代码完整的执行(面对pthread_cancel互斥量不能做到这一点

  • 在要完整执行的代码的前后,设置禁用pthread_cancel
  • 在对要求完整执行的线程发出取消命令之后,用join等返回

    2. 线程send发送信号给digest让其运行,但如果digest不在运行状态,则该信号丢失 线程send在收到cancel之后不保证事件处理,直接退出
    3.
线程send在收到cancel之后不保证事件处理,直接退出
    4.
digest线程不再等待而在执行过程时(比如sleep)时,sender发送的信号被丢失
    5. sender
发送5个信号给digest,可能digest最终只能消化3个信号,它只保证这三个信号被完整执行,而不中断,但不保存所有的信号都被执行。可能通过输出看到digest在取消信号发出之后依然执行完成最后的一次处理。
    6.
如果要保证所有发出的信号都被执行,有两种方式:

  • digest完成自己的信号之后发送信号给sender(在等待)
  • 利用消息队列,digest从消息队列中取出sender不断发出的信号,依次处理,处理完后退出

首先来看看整个目录的结构:
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;
}


在清除处理函数中等待不是一个好主意(因为毕竟该线程是用来清除的,而不是用来等待某件事的发生)。
但是如果你的清除处理函数确实需要等待一些东西,它也会工作得很好。

 

 

阅读(1101) | 评论(0) | 转发(0) |
0

上一篇:pthread之工作模式

下一篇:POSIX 线程详解

给主人留下些什么吧!~~