多线程程序设计3

来源:互联网 发布:经传软件捕捞季节 编辑:程序博客网 时间:2024/05/29 08:59

 

说明:只供学习交流,装载请注明出处

 

五,线程清除

线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit或线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是在其他线程的干预下,或者由于自身出错(比如访问非法地址)而退出,退出方式是不可预见的。不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,如何保证线程终止时能顺利的释放掉自己所占用的资源,是一个必须考虑解决的问题。

 

为了解决上述问题,在线程API中提供了pthread_cleanup_pushpthread_cleanup_pop()函数用于自动释放资源。使用时,只需将待处理的线程代码放在pthread_cleanup_push函数和pthread_cleanup_pop函数之间,当程序运行在它们之间发生终止动作(包括调用pthread_exit和非正常退出,但不包括return)时,都将执行pthread_cleanup_push()所指定的清理函数进行线程清除工作。

pthread_cleanup_push函数

头文件

#include <pthread.h>

函数原型

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

返回值

函数功能:将清除函数压入清除栈,标志一个线程清理段的开始。

参数说明:routine为线程清除函数,arg为线程清除函数的传入参数。

pthread_cleanup_pop函数

头文件

#include <pthread.h>

函数原型

void pthread_cleanup_pop(int execute);

返回值

函数功能:将清除函数弹出清除栈。

参数说明:程序执行到pthread_cleanup_pop()时在弹出清理函数的同时若execute为非0:执行清理函数, execute0:不执行清理函数。

 

实例:

#include <stdio.h>#include <pthread.h>#include <unistd.h>/*线程清理函数*/void *clean(void *arg){    printf("cleanup :%s\n",(char *)arg);    return (void *)0;}/*线程1的执行函数*/void *thr_fn1(void *arg){    printf("thread 1 start  \n");/*将线程清理函数压入清除栈两次*/    pthread_cleanup_push( (void*)clean,"thread 1 first handler");    pthread_cleanup_push( (void*)clean,"thread 1 second hadler");    printf("thread 1 push complete  \n");    if(arg)    {        return((void *)1); //线程运行到这里会结束,后面的代码不会被运行。由于是用return退出,所以不会执行线程清理函数。    }    pthread_cleanup_pop(0);    pthread_cleanup_pop(0);    return (void *)1;}/*线程2的执行函数*/void *thr_fn2(void *arg){    printf("thread 2 start  \n");/*将线程清理函数压入清除栈两次*/    pthread_cleanup_push( (void*)clean,"thread 2 first handler");    pthread_cleanup_push( (void*)clean,"thread 2 second handler");    printf("thread 2 push complete  \n");    if(arg)    {        pthread_exit((void *)2);//线程运行到这里会结束,后面的代码不会被运行。由于是用pthread_exit退出,所以会执行线程清理函数。执行的顺序是先压进栈的后执行,即后进先出。    }    pthread_cleanup_pop(0);    pthread_cleanup_pop(0);    pthread_exit((void *)2);}int main(void){    int err;    pthread_t tid1,tid2;    void *tret;/*创建线程1并执行线程执行函数*/    err=pthread_create(&tid1,NULL,thr_fn1,(void *)1);    if(err!=0)    {        printf("error .... \n");        return -1;    }/*创建线程2并执行线程执行函数*/    err=pthread_create(&tid2,NULL,thr_fn2,(void *)1);if(err!=0)    {        printf("error .... \n");        return -1;    }/*阻塞等待线程1退出,并获取线程1的返回值*/    err=pthread_join(tid1,&tret);    if(err!=0)    {        printf("error .... \n");        return -1;    }    printf("thread 1 exit code %d  \n",(int)tret);/*阻塞等待线程2退出,并获取线程2的返回值*/    err=pthread_join(tid2,&tret);    if(err!=0)    {        printf("error .... ");        return -1;    }printf("thread 2 exit code %d  \n",(int)tret);        return 1;}运行结果:[root@localhost test]# ./pthread_cleanup thread 1 start...thread 1 push complete...thread 2 start...thread 2 push completecleanup: thread 2 second handercleanup: thread 2 first handerthread 1 exit code 1thread 2 exit code 2[root@localhost test]#


 

六,线程同步

多进程对共享资源进行访问的时候,必须保证某个时刻只有一个进程访问资源,这个问题在多线程情况下,同样是存在的。为此,Linux系统提供了互斥锁来保证某个时刻只有一个线程使用资源。

互斥锁提供了在多线程情况下相互排斥的方法。举例来说,如有两个线程,线程A和线程B,当线程A意图锁定一个互斥锁时,线程B在线程A操作之前就已经完成了锁定,这时,线程A将进入睡眠状态,直到线程B释放该锁才能进行锁定。通过对互斥锁进行锁定操作,就能实现进程对共享资源的依次访问。

 

互斥锁的锁定和解锁是通过pthread_mutex_lock函数和pthread_mutex_unlock函数来实现的。互斥锁一般用来保护数据结构。通过线程对互斥锁的锁定和解锁,能够实现某一时刻只有一个线程访问该数据结构。

 

互斥锁操作主要有3种行为,分别是加锁、解锁和测试加锁。当对互斥锁进行了加锁操作,在没有进行解锁之前,任何线程都没有办法获得互斥锁。

 

要创建互斥锁,可以通过两种途径来实现:一种是通过POSIX标准中定义的宏来实现对互斥锁的初始化,具体实现如下:

pthread_mutex_t mutex =PTHREAD_MUTEX_INITIALIZER;

另一种方法是调用pthread_mutex_init函数来实现互斥锁的初始化,该函数的具体信息如下表:

pthread_mutex_init函数

 

头文件

#include <pthread.h>

函数原型

int pthread_mutex_init(pthread_mutex_t  *mutex,

const pthread_mutexattr_t attr);

返回值

成功

失败

是否设置errno

0

0

说明:pthread_mutex_init函数将根据attr中给出的属性初始化互斥锁。如果attr参数为NULL,表示使用默认的属性。pthread_mutex_init函数调用成功后,互斥锁将完成初始化,并处于解锁状态。

对一个已经初始化的互斥锁再次初始化会产生不可预测的后果。

使用PTHREAD_MUTEX_INITIALIZER宏来初始化互斥锁,其效果等同于调用pthread_mutex_init函数时将attr参数指定为NULL

 

错误信息:

EBUSY:试图初始化已经初始化的互斥锁。

EINVAL:参数attr中给出的参数非法。

 

举例:

pthread_mutex_t my_lock;

pthread_mutex_init(&my_lock, NULL);

 

互斥锁的加锁:

pthread_mutex_lock函数用于对互斥锁进行加锁操作,其具体信息如下表:

pthread_mutex_lock函数

 

头文件

#include <pthread.h>

函数原型

int pthread_mutex_lock(pthread_mutex_t  *mutex);

返回值

成功

失败

是否设置errno

0

0

说明:pthread_mutex_lock函数使用较为简单,参数mutex为要操作的互斥锁。

如果mutex的锁类型为PTHREAD_MUTEX_NORMAL,函数将不提供死锁的检测,因此,加锁操作有可能导致出现死锁。如果线程试图对一个没有加锁的互斥锁进行解锁操作,将导致不可预测的行为。

如果mutex的锁类型为PTHREAD_MUTEX_ERRORCHECK,即检错锁,该锁将提供错误检测能,当线程试图对一个已经加锁的互斥锁进行加锁操作时,会得到错误信息。

如果mutex的锁类型为PTHREAD_MUTEX_RECURSIVE,即嵌套锁,该锁充许同一个线程对锁进行多次的获取,并可以进行多次unlock解锁操作,当不同线程对该锁进行操作时,则在加锁线程解锁时重新竞争。进行一次加锁操作后,锁计数器将加1;当进行一次解锁后,计数器减1,直到锁计数器为0,其他线程才可以获得该互斥锁。

如果mutex的锁类型为PTHREAD_MUTEX_DEFAULT,即为普通锁,当一个线程加锁以后,其余请求锁的线程将形成一个等待队列,并在解锁后按优先级获得锁,这种策略保证了资源分配的公平性。

 

错误信息:

EINVAL:参数mutex未指向初始化的互斥锁。

EAGAIN:未获得互斥锁。

EDEADLK:当前线程已经拥有互斥锁。

 

pthread_mutex_trylock函数与pthread_mutex_lock函数功能类似,同样用于实现对互斥锁的加锁操作。

 

 

pthread_mutex_trylock函数

 

头文件

#include <pthread.h>

函数原型

int pthread_mutex_trylock(pthread_mutex_t  *mutex);

返回值

成功

失败

是否设置errno

0

0

说明:pthread_mutex_trylock函数用来锁住mutex所指向的互斥体,但不阻塞。如果该互斥体已经被上锁,该调用不会阻塞等待,而会返回一个错误代码。如果锁类型是嵌套锁(PTHREAD_MUTEX_RECURSIVE),且调用进程对互斥锁已经加锁,调用该函数将使得锁计数器加1

 

错误信息:

EINVAL:参数mutex为指向初始化的互斥锁。

EAGAIN:为获得互斥锁。

EDEADLK:当前线程已经拥有互斥锁。

EBUSY:由于互斥锁已经加锁,无法获得该互斥锁。

 

 

互斥锁的解锁:

Pthread_mutex_unlock函数用于对互斥锁进行解锁操作,该函数的具体信息如下表:

 

头文件

#include <pthread.h>

函数原型

int pthread_mutex_unlock(pthread_mutex_t  *mutex);

返回值

成功

失败

是否设置errno

0

0

说明:pthread_mutex_unlock函数将释放参数mutex中指向的互斥锁,释放方式依赖于互斥锁的锁类型。

 

错误信息:

EINVAL:参数mutex未指向初始化的互斥锁。

EAGAIN:未获得互斥锁。

EPERM:当前线程不拥有互斥锁。

 

实例:

程序给出了使用互斥锁实现线程同步的实例。在程序中,两个线程对共同的全局变量g_var进行加1操作。为了保证结果的正确性,必须使得在某个时刻只有一个线程对该全局变量进行操作,在程序中通过互斥锁实现了这一点。具体代码如下:

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <pthread.h>int g_var;pthread_mutex_t locker = PTHREAD_MUTEX_INITIALIZER;void *thread_function(void){int i,j;for (i = 0; i < 4; i++){pthread_mutex_lock(&locker);j = g_var;j = j+1;printf("---in thread ...increase one---\n");fflush(stdout);sleep(1);g_var = j;pthread_mutex_unlock(&locker);}return (NULL);}int main(void){pthread_t thread_id;int i;if (pthread_create(&thread_id, NULL, thread_function, NULL)){perror("Cannot create new thread");return (1);}for (i = 0; i < 4; i++){pthread_mutex_lock(&locker);g_var = g_var + 1;pthread_mutex_unlock(&locker);printf("...in main thread...increase one...\n");fflush(stdout);sleep(1);}if (pthread_join(thread_id, NULL)){perror("Cannot join thread.");return (1);}printf("g_var: %d\n", g_var);return (0);}运行结果:[root@localhost test]# ./mutex ---in thread ...increase one------in thread ...increase one------in thread ...increase one------in thread ...increase one---...in main thread...increase one......in main thread...increase one......in main thread...increase one......in main thread...increase one...g_var: 8[root@localhost test]#


 

 

 

 

 

 

 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 身上起小包很痒怎么办 浑身起红包很痒怎么办 手太粗糙怎么办小窍门 小腿长疙瘩很痒怎么办 腿过敏起红疙瘩怎么办 肚子上起红疙瘩很痒怎么办 小蚂蚁咬了肿了怎么办 锦鲤鱼尾巴烂了怎么办 泰迪身上长白毛怎么办 鱼身上有红斑了怎么办 新买锦鲤不吃食怎么办 鱼身上有红血丝怎么办 大腿内侧有红色条纹怎么办 腿上出现红血丝怎么办 孕妇有脚气,很痒怎么办 孕晚期脚气很痒怎么办 孕期有脚气很痒怎么办 激光后留下色沉怎么办 腋下很黑怎么办怎样才能变白 屁股上长一块癣怎么办 鼻两侧一热发红怎么办 脸上起皮发红痒怎么办 自癜风发红发痒怎么办 脸又干又痒怎么办 脸发红还有点痛怎么办 脸过敏了红痒怎么办 脸两边一片红痒怎么办 婴儿大腿内侧破皮怎么办 宝宝大腿根淹了怎么办 大腿一走路就疼怎么办 下面痒怎么办用什么洗 长藓怎么办用什么药膏 小腿长湿疹很痒怎么办 产后排不出大便怎么办 3岁宝宝大便不通怎么办 腿上干燥像鱼鳞怎么办 一岁脸上长癣怎么办 脖子长了一片癣怎么办 深圳摇到车牌后怎么办 发现车被套牌了怎么办 我车牌被套牌了怎么办