多线程程序设计3

 

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

 

五,线程清除

线程终止有两种情况:正常终止和非正常终止。线程主动调用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 complete
cleanup: thread 2 second hander
cleanup: thread 2 first hander
thread 1 exit code 1
thread 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]#


 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值