Chinaunix

标题: pthread_cancel 的问题 [打印本页]

作者: andyY    时间: 2006-02-08 11:28
标题: pthread_cancel 的问题
很简单的情况,创建两个线程,第2个去cancel第1个。
但如果 f1 中的 localtime_r 和 sem 同时打开执行, f2 在 join时阻塞;
如果只开任何一个, f2 的join 成功。
困惑中,请高手指点! (redhat 8.0)

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

#define LEN 128
#define PATH "./"

pthread_t tid[2];

char filename[LEN];
FILE *f;

void *f1(void *p)
{
    pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    printf( "in %s\n", __FUNCTION__ );
    time_t ta;
    struct tm tb;

    ta = time( NULL );
#if 1
    localtime_r( &ta, &tb );
#else
    memset( &tb, 0, sizeof(tb) );
#endif
    snprintf( filename, LEN, "%s%d_%d_%d-%d_%d.data",
                        PATH,
                        tb.tm_year + 1900, tb.tm_mon, tb.tm_mday,
                        tb.tm_hour, tb.tm_min);

    printf( "Backup file is  %s\n", filename);
    f = fopen( filename, "w" );
    if( ! f)
        perror( "ERROR: OpenBackup" );


#if 1
    sem_t sem;
    sem_init( &sem, 0, 0);
    sem_wait( &sem );
#else
    sleep(1000);
#endif
}

void *f2(void *p)
{
    pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    printf( "in %s\n", __FUNCTION__ );

    pthread_cancel( tid[0] );
    printf( "f2: join ...\n");
    pthread_join( tid[0], NULL);

    printf( "f2: join ok !\n" );
}


int main()
{

  pthread_attr_t tattr;
  pthread_attr_init( &tattr);
  pthread_attr_setdetachstate( &tattr, PTHREAD_CREATE_JOINABLE);

  pthread_create( &tid[0], &tattr, f1, NULL);
  sleep(5);
  pthread_create( &tid[1], &tattr, f2, NULL);

  printf( "press ...\n" );
  getchar();
}

[ 本帖最后由 andyY 于 2006-2-8 11:31 编辑 ]
作者: mq110    时间: 2006-02-08 11:34
f2的阻塞原因应该是 f1中sem_wait( &sem );的原因吧.
没有在其他线程中sem_post的原因.
作者: andyY    时间: 2006-02-08 11:38
多谢版主的回复。

f2 还有其它工作,用 sem 来作阻塞,完整的是有其它线程 post这个全局sem的。这里简化了下。
但这些都是正常使用,线程阻塞一样可以被 cancel 吧!
作者: mq110    时间: 2006-02-08 11:42
原帖由 andyY 于 2006-2-8 11:38 发表
多谢版主的回复。

f2 还有其它工作,用 sem 来作阻塞,完整的是有其它线程 post这个全局sem的。这里简化了下。
但这些都是正常使用,线程阻塞一样可以被 cancel 吧!


至于阻塞是否能被cancel掉.要看具体的实现了.
我也去查查相关的资料.
我一直用多进程的模型.

能说说cancel的真正目的是什么吗? 多谢.
作者: liuzhuan23    时间: 2006-02-08 12:11
pthread_cancel() 在posix中有叙述:将其称之为“不安全的上下文”

一个thread中,处于完全封闭的环境,没有任何代码出口,呵呵,pthread_cancel的做法就值得怀疑了,安全吗?

我一直坚持认为pthread_cancel是个危险的函数,如果在代码中遇到需要停止thread的流程,我都要在thread的代码中预留一个标记以便thread的退出:
while(1)
{
    if( thread_stop == 1 )
    {
         pthread_exit(0);
    }
}

强烈建议thread的出口使用pthread_exit(), 不要简单的使用return,这二者之间对于代码现场的清理是不同的
作者: mq110    时间: 2006-02-08 12:24
原帖由 liuzhuan23 于 2006-2-8 12:11 发表
pthread_cancel() 在posix中有叙述:将其称之为“不安全的上下文”

一个thread中,处于完全封闭的环境,没有任何代码出口,呵呵,pthread_cancel的做法就值得怀疑了,安全吗?

我一直坚持认为pthread_cance ...


说的有一定道理. 我想轮旬的机制有些浪费资源.应该采取其他方式.

我想LZ 不能pthread_cancel的原因 是否应该理解为.持有信号量不可以被cancel??
作者: andyY    时间: 2006-02-08 12:25
5楼说得有些道理。不过当
while(1)
{
    if( thread_stop == 1 )
    {
         ...//含阻塞的业务过程
         pthread_exit(0);
    }
}
如果持续阻塞,thread_stop 没法检查退出!

另外thread的封闭性有 pthread_cleanup_xx 来作些处理:Cleanup handlers are functions that get called  when  a  thread  termi- nates,  either  by  calling pthread_exit(3) or !!! because of cancellation !!!.
通常我是配合用,应该没有问题的。
作者: andyY    时间: 2006-02-08 12:28
如果大家方便,最好编译运行下.
我最奇怪的是 localtime_r 和 sem_wait() 的共同执行能影响线程1(f1)能否被线程2(f2)cancel掉。而sem_wait 又是一个cancellation point.
作者: liuzhuan23    时间: 2006-02-08 12:37
1 sem_wait的状态是可以被signal中断
2 sem_wait的状态可不可以被cancel中断我没有试验过,所以我也不能下结论
3 从LZ的代码流程来看,很明显遇到了sem_wait指令之后并没有sem_post,导致cancel阻塞
4 斑竹说:“我想轮旬的机制有些浪费资源.应该采取其他方式”,呵呵,这我暂时还没有太好的方法^_^,如果斑竹是担心时钟周期占用的问题,使用其它辅助手段可以缓解
作者: andyY    时间: 2006-02-08 12:43
man sem_wait:
  CANCELLATION
       sem_wait is a cancellation point.
  不管是否sem_post, 始终是cancellation point.

程序的结果:
1、仅执行 localtime_r ,可被cancel
2、仅执行 sem_wait, 可被cancel
3、同时执行 localtime_r 和sem_wait, 不能被cancel
这是怎么回事呢?

void *f1(void *p)
{
    pthread_setcancelstate( PTHREAD_CANCEL_ENABLE, NULL);
    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);

    printf( "in %s\n", __FUNCTION__ );
    time_t ta;
    struct tm tb;

    ta = time( NULL );
#if 1
    localtime_r( &ta, &tb );
#else
    memset( &tb, 0, sizeof(tb) );
#endif
   
   //...

#if 1
    sem_t sem;
    sem_init( &sem, 0, 0);
    sem_wait( &sem );
#else
    sleep(1000);
#endif
}
作者: hades555    时间: 2006-02-08 12:54
你运行之后是什么结果?
我运行后in f2
f2:join 说明cancel成功了?
作者: andyY    时间: 2006-02-08 12:56
printf( "f2: join ok !\n" );
作者: hades555    时间: 2006-02-08 12:58
原帖由 andyY 于 2006-2-8 12:56 发表
printf( "f2: join ok !\n" );


我直接运行的你的代码.

in f1
Backup file is  ./2006_1_8-13_3.data
press ...
in f2
f2: join ...
f2: join ok !

以上是运行结果.
作者: andyY    时间: 2006-02-08 13:03
不可能吧,是从第一篇copy的吗?
作者: mq110    时间: 2006-02-08 13:03
原帖由 andyY 于 2006-2-8 13:03 发表
不可能吧,是从第一篇copy的吗?


我的也成功了.
作者: andyY    时间: 2006-02-08 13:07
晕! linux 2.4.20 , gcc 3.2.2  ?
作者: mq110    时间: 2006-02-08 13:09
[root@Ora9i tmp]# uname -r
2.6.9-5.ELsmp
[root@Ora9i tmp]# gcc -v
Reading specs from /usr/lib/gcc/i386-redhat-linux/3.4.3/specs
Configured with: ../configure --prefix=/usr --mandir=/usr/share/man --infodir=/usr/share/info --enable-shared --enable-threads=posix --disable-checking --with-system-zlib --enable-__cxa_atexit --disable-libunwind-exceptions --enable-java-awt=gtk --host=i386-redhat-linux
Thread model: posix
gcc version 3.4.3 20041212 (Red Hat 3.4.3-9.EL4)


估计是你内核版本太低. 那个时候系统对pthread库的实现还不完善.




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2