Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59065
  • 博文数量: 20
  • 博客积分: 1410
  • 博客等级: 上尉
  • 技术积分: 200
  • 用 户 组: 普通用户
  • 注册时间: 2009-11-30 11:11
文章分类

全部博文(20)

文章存档

2018年(2)

2010年(2)

2009年(16)

我的朋友

分类:

2009-11-30 11:45:17

linux内进程是操作系统分配资源的实体,而线程是分配时间执行的实体。线程共享进程的某些资源,如地址空间,但是各个线程(各个用户线程对应的内核线程)在内核中的堆栈是独立的。
#include
pthread_t pthread_self(void);//返回自身线程id
int pthread_equal(pthread_t tid1,pthread_t tid2);//比较两个线程id是否相等
//成功创建返回0,如果创建失败返回的是错误编号,而不是像其他linux函数一样返回-1并设置errno,所有线程都共享errno全局变量,这样会造成混乱。
int pthread_create (pthread_t *__restrict  __newthread,
                           __const pthread_attr_t *__restrict  __attr,
                           void *(*__start_routine) (void *),
                           void *__restrict __arg) __THROW __nonnull ((1, 3));
//__rval指向的区域存储退出信息。线程运行结束后linux内核保存的只是存储退出信息区的首地 址,而并未将退出信息实际保存在内核中,因此在线程结束后,其保存退出信息的内存区域仍然是有效的,所以不能将退出信息存储在局部变量中,而应该使用动态 分配的内存或全局变量。
void pthread_exit(void *__retval);
//__th是等待的线程id,__thread_return取得被等待线程的信息
//如果成功得到指定线程退出信息,返回0。失败返回错误编号
void pthread_join(pthread_t __th, void **__thread_return);
//-------------------- 一个例子 -----------------------------
#include
#include
#include
struct a{
        int b;
        int c;
};
void *thread1(void *arg)
{       struct a *r4=(struct a*)arg;
        printf("first thread\n");
        r4->b=20;
        r4->c=21;
        return (void*)r4;
}

int main()
{
        pthread_t tid1;
        int err;
        void *res;
        void *res;
        struct a r1;
        r1.b=10;
        r1.c=11;
        err=pthread_create(&tid1,NULL,thread1,(void*)&r1);
        if(err!=0)
        {       printf("can't create thread1 %s\n",strerror(err));
                exit(1);
        }
        err=pthread_join(tid1,&res);
        if(err!=0)
        {       printf("can't join thread1 %s\n",strerror(err));
                exit(1);
        }
        printf("result from thread1: %d %d\n",((struct a*)res)->b,((struct a*)res)->c);
        printf("result from main: %d %d\n",(r1).b,(r1).c);
        return 0;
}
//------------------------------------------------

//取消tid为__th的线程
//取消成功返回0,失败返回错误编号
int pthread_cancel (pthread_t __th);
//------------------例子------------------------------
void *thread1(void *arg)
{
        printf("first thread\n");
        sleep(10);
        return (void*)1;
}

int main()
{
        pthread_t tid1;
        int err;
        void *res;
        err=pthread_create(&tid1,NULL,thread1,NULL);
        if(err!=0)
        {       printf("can't create thread1 %s\n",strerror(err));
                exit(1);
        }
        err=pthread_cancel(tid1);
        if(err!=0)
        {       printf("can't cancel thread1 %s\n",strerror(err));
                exit(1);
        }
        err=pthread_join(tid1,&res);
        if(err!=0)
        {       printf("can't join thread1 %s\n",strerror(err));
                exit(1);
        }
        if(res==PTHREAD_CANCELED)
                printf("cancel from main %u\n",(unsigned int)tid1);
        else
                printf("error");
        return 0;
}
//------------------------------------------------
//routine是入栈的清理函数指针,arg是routine的参数
void pthread_cleanup_push(void (*routine)(void *),void *arg);
//execute为非零值是弹出清理函数并执行,为0时弹出清理函数但不执行
void pthread_cleanup_pop(int execute);

栈中的清理函数会在以下3种情况下执行(线程中用return退出不会执行):
*调用pthread_exit()退出
*线程被其它进程取消
*使用非零参数调用pthread_cleanup_pop()

//----------------------pthread related------------------------
//使用pthread.h时gcc参数加 -lpthread

//修改线程的属性
  用pthread_create函数创建了一个线程,在这个线程中,我们使用了默认参数,即将该函数的第二个参数设为NULL。的确,对大多数程序来说,使用默认属性就够了,但我们还是有必要来了解一下线程的有关属性。

  属性结构为pthread_attr_t,它同样在头文件/usr/include/pthread.h中定义,喜欢追根问底的人可以自己去查看。属 性值不能直接设置,须使用相关函数进行操作,初始化的函数为pthread_attr_init,这个函数必须在pthread_create函数之前调 用。属性对象主要包括是否绑定、是否分离、堆栈地址、堆栈大小、优先级。默认的属性为非绑定、非分离、缺省1M的堆栈、与父进程同样级别的优先级。

  关于线程的绑定,牵涉到另外一个概念:轻进程(LWP:Light Weight Process)。轻进程可以理解为内核线程,它位于用户层和系统层之间。系统对线程资源的分配、对线程的控制是通过轻进程来实现的,一个轻进程可以控制 一个或多个线程。默认状况下,启动多少轻进程、哪些轻进程来控制哪些线程是由系统来控制的,这种状况即称为非绑定的。绑定状况下,则顾名思义,即某个线程 固定的"绑"在一个轻进程之上。被绑定的线程具有较高的响应速度,这是因为CPU时间片的调度是面向轻进程的,绑定的线程可以保证在需要的时候它总有一个轻进程可用。通过设置被绑定的轻进程的优先级和调度级可以使得绑定的线程满足诸如实时反应之类的要求。

  设置线程绑定状态的函数为 pthread_attr_setscope,它有两个参数,第一个是指向属性结构的指针,第二个是绑定类型,它有两个取值: PTHREAD_SCOPE_SYSTEM(绑定的)和PTHREAD_SCOPE_PROCESS(非绑定的)。下面的代码即创建了一个绑定的线程。

#include
pthread_attr_t attr;
pthread_t tid;

/*初始化属性值,均设为默认值*/
pthread_attr_init(&attr);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);

pthread_create(&tid, &attr, (void *) my_function, NULL);

  一个可能常用的属性是线程的优先级,它存放在结构sched_param中。用函数pthread_attr_getschedparam和函数 pthread_attr_setschedparam进行存放,一般说来,我们总是先取优先级,对取得的值修改后再存放回去。下面即是一段简单的例子。

#include
#include
pthread_attr_t attr;
pthread_t tid;//一定要这样写,因为构造函数会分配内存,如果写成pthread_t* ptid,则后面调用pthread_create()会出现段错误
sched_param param;
int newprio=20;
pthread_attr_init(&attr);
pthread_attr_getschedparam(&attr, ¶m);
param.sched_priority=newprio;
pthread_attr_setschedparam(&attr, ¶m);
pthread_create(&tid, &attr, (void *)myfunction, myarg);
//----------------------pthread related------------------------
//互斥量初始化函数,如果第二个参数为NULL内核使用默认属性对互斥量进行初始化
//如果初始化成功返回0,失败返回错误编号
int pthread_mutex_init (pthread_mutex_t *__mutex,
                               __const pthread_mutexattr_t *__mutexattr) __THROW __nonnull ((1));
//销毁
int pthread_mutex_destory(pthread_mutex_t *__mutex);
//如果互斥量__mutex的锁已经被某一个线程得到,那么该函数会导致线程阻塞,直到__mutex的锁被释放
//成功得到锁,返回0;失败返回错误编号
int pthread_mutex_lock(pthread_mutex_t *__mutex);
//释放互斥量的锁
//释放成功返回0,失败返回错误编号
int pthread_mutex_unlock(pthread_mutex_t *__mutex);
//尝试对互斥量加锁。如果成功加锁返回0;如果互斥量__mutex的锁已经被某一个线程得到,那么该函数会马上返回EBUSY而不会阻塞
int pthread_mutex_trylock(pthread_mutex_t *__mutex);
//----------------------例子------------------------
暂无

//--------------------------------------------------
//初始化读写锁。__rwlock在函数内被初始化,然后返回调用者。__attr为NULL时内核使用默认值对读写锁进行初始化。
//如果初始化成功返回0;失败返回错误编号
int pthread_rwlock_init (pthread_rwlock_t *__restrict __rwlock,
                                __const pthread_rwlockattr_t *__restrict __attr) __THROW __nonnull ((1));
//成功销毁返回0;失败返回错误编号
int pthread_rwlock_destroy (pthread_rwlock_t *__rwlock) __THROW __nonnull ((1));
//读模式锁函数。如果__rwlock已经被某一个线程在写模式写得到,或者有一个线程在写模式下等待该锁,此函数会使调用线程阻塞
//如果能得到读模式锁返回0;失败返回错误编号
int pthread_rwlock_rdlock (pthread_rwlock_t *__rwlock) __THROW __nonnull ((1));
//如果__rwlock已经被某一个线程在写模式写得到,或者有一个线程在写模式下等待该锁,此函数会马上返回EBUSY,调用线程不会阻塞
//如果能得到读模式锁返回0;失败返回错误编号EBUSY
int pthread_rwlock_tryrdlock (pthread_rwlock_t *__rwlock) __THROW __nonnull ((1));
//写模式锁函数。如果测试读写锁时有任意一线程占有该锁,此函数会阻塞
//如果能得到写模式锁返回0;失败返回错误编号
int pthread_rwlock_wrlock (pthread_rwlock_t *__rwlock) __THROW __nonnull ((1));
//如果测试读写锁时有任意一线程占有该锁,马上返回EBUSY
//如果能得到写模式锁返回0;失败返回错误编号EBUSY
int pthread_rwlock_trywrlock (pthread_rwlock_t *__rwlock) __THROW __nonnull ((1));
//无论在读写模式下占有锁都用此函数释放锁。
//成功释放返回0;失败返回错误编号
int pthread_rwlock_unlock (pthread_rwlock_t *__rwlock) __THROW __nonnull ((1));

//初始化线程属性结构。attr指向属性结构的首地址,该函数为属性分配内存空间。
//如果成功初始化了属性结构函数返回0,失败返回错误编号
int pthread_attr_init(pthread_attr_t *attr);

//此函数释放attr指向的属性结构空间
//如果成功初始化了属性结构函数返回0,失败返回错误编号
int pthread_attr_destroy(pthread_attr_t *attr);
设置为分离状态的线程不需要被join,如果被join会马上返回错误编号。下面的例子通过修改创建线程的默认属性创建一个分离的线程
//--------------------创建一个分离的线程的例子------------------------
#include
#include
#include

void *thread1(void *arg)
{
        printf("first thread\n");
        pthread_cleanup_push(cleanup,NULL);
        sleep(3);
        pthread_cleanup_pop(1);

        return (void*)1;
}

int main()
{
        pthread_t tid1;
        pthread_attr_t attr;
        int err;
        void *res;
        err=pthread_attr_init(&attr);
        if(err!=0)
        {       printf("can't init attr %s\n",strerror(err));
        }
        pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
        if(err!=0)
        {       printf("can't set attr %s\n",strerror(err));
        }

        err=pthread_create(&tid1,&attr,thread1,NULL);
        if(err!=0)
        {       printf("can't create thread %s\n",strerror(err));
                exit(1);
        }
        err=pthread_join(tid1,NULL);//返回的错误编码表明tid1参数错误
        if(err!=0)
        {       printf("can't join thread1 %s\n",strerror(err));
                exit(1);
        }
        
        pthread_attr_destroy(&attr);
        pthread_exit(NULL);
}
//------------------------------------------------
也可以再main线程中直接分离以运行的线程,用下面这个函数
int pthread_detach(pthread_t tid);

    线程的分离状态决定一个线程以什么样的方式来终止自己。线程的默认属性为非分离状态,这种情况下,原有的线程等待创建的线程结束。只有当 pthread_join()函数(这个函数实际上就是暂停调用它的进程/线程的执行,直到第一个参数指定的线程终止)返回时,创建的线程才算终止,才能 释放自己占用的系统资源。而分离线程不是这样子的,它没有被其他的线程所等待,自己运行结束了,线程也就终止了,马上释放系统资源。程序员应该根据自己的 需要,选择适当的分离状态。设置线程分离状态的函数为 pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)。 第二个参数可选为PTHREAD_CREATE_DETACHED(分离线程)和 PTHREAD _CREATE_JOINABLE(非分离线程)。这里要注意的一点是,如果设置一个线程为分离线程,而这个线程运行又非常快,它很可能在 pthread_create函数返回之前就终止了,它终止以后就可能将线程号和系统资源移交给其他的线程使用,这样调用pthread_create的 线程就得到了错误的线程号。要避免这种情况可以采取一定的同步措施,最简单的方法之一是可以在被创建的线程里调用 pthread_cond_timewait函数,让这个线程等待一会儿,留出足够的时间让函数pthread_create返回。设置一段等待时间,是 在多线程编程里常用的方法。但是注意不要使用诸如wait()之类的函数,它们是使整个进程睡眠,并不能解决线程同步的问题。
    一个线程可以是可加入的(joinable thread)或者是可分离的(detached thread)。可加入的线程在结束后,如果没有别的线程对其使用pthread_join,则它会挂起并等待,直到有线程调用 pthread_join,它的资源才会被释放。可分离的线程结束后,它的资源可被立即回收,不用等到被jion时才释放资源。但缺点是别的线程无法通过 pthread_join来保证和它的同步,或者获得它的返回值。
 

如果main线程return(实际上调用了__exit()),这个进程的所有线程到会结束。所以在不能保证线程什么时候结束时,如果要退出main线程,应该调用pthread_exit()。



线程可杀有三种状态:

    * 异步可杀的(asynchronously cancelable):线程可以在任何时间被杀。
    * 同步可杀的(synchronously cancelable):线程可以被杀,在到达某一个特定的位置之后死掉。我们称这些特定的位置为扼杀点(cancellation points)。
    * 无敌的(uncancelable):免疫一切暗杀魔法,反正就是杀不掉。
线程创建后的默认状态是同步可杀的。我们可以使用pthread_setcanceltype来将线程的扼杀状态设为上面三种的一种。对于扼杀点的创建, 函数 pthread_testcancel可以创建,这个函数除了创建扼杀点之外不做任何事情。另外,一些其他函数的内部实现可能会创建扼杀点,所以调用这些 函数也会相当于创建了一个扼杀点。可以参考pthread_cancel的man page来查看这些函数的列表。
线程保护自己的更为强大的一种手段是pthread_setcancelstate函数,该函数可以禁止/激活扼杀模式。在禁止扼杀模式的情况下,我们可 以放心的写一些critical section的代码。需要注意的是,在离开critical section后,应立即恢复到原来的扼杀模式。
阅读(1396) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~