多线程程序设计3
来源:互联网 发布:经传软件捕捞季节 编辑:程序博客网 时间:2024/05/29 08:59
说明:只供学习交流,装载请注明出处
五,线程清除
线程终止有两种情况:正常终止和非正常终止。线程主动调用pthread_exit或线程函数中return都将使线程正常退出,这是可预见的退出方式;非正常终止是在其他线程的干预下,或者由于自身出错(比如访问非法地址)而退出,退出方式是不可预见的。不论是可预见的线程终止还是异常终止,都会存在资源释放的问题,如何保证线程终止时能顺利的释放掉自己所占用的资源,是一个必须考虑解决的问题。
为了解决上述问题,在线程API中提供了pthread_cleanup_push和pthread_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:执行清理函数, execute为0:不执行清理函数。
实例:
#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
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 多线程程序设计
- 《Win32多线程程序设计》(3)---同步控制
- Java 多线程程序设计要点
- 读书笔记:多线程程序设计要点
- Java多线程程序设计入门
- Java多线程程序设计
- Java多线程程序设计
- Java多线程程序设计
- 详细讲解新浪微博的API到底如何使用
- 学习新技术的10个建议
- Eclipse Theme
- 动态规划算法——乘法表问题
- 黑马程序员--多线程与套接字组合:简单聊天室(2)
- 多线程程序设计3
- Remove Duplicates from Sorted Array II
- svn(服务器端和客户端)
- 黑马程序员之Properties类
- 如何将linux下home目录下的中文名改成英文名
- linux指定运行时动态库路径
- 编程语言那些事
- Java常用输入输出流
- BCompare 应用程序发生错误 解决办法