Linux/UNIX系统编程手册---第30章线程同步之互斥量

保护对共享变量的访问:互斥量

一、 以非原子方式访问共享资源实例

#include <pthread.h>
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/**********************************************
 * 代码描述
该程序创建了两个线程,且均执行同一函数。该函数执行一个循环,重复以下步骤:将glob复制
到本地变量loc中,然后递增loc,再把loc复制回glob,以此不断增加全局变量glob的值。
因为loc是分配于线程栈中的自动变量(automatic variable),所以每个线程都有一份。循环重
复的次数要么由命令行参数指定,要么取默认值
 * ********************************************
 * ***********************************/
static int glob = 0;
static void * threadFunc(void *arg)
{
	int loops = *((int *)arg);
	int loc,j;
	for(j=0;j<loops;j++){
		loc = glob;
		loc++;
		glob = loc;
	}
    return NULL;
}
int main(int argc,char *argv[])
{
	pthread_t t1,t2;
	int s,loops; 
	//式A?B:C值为:若A为真,则B;若A为假,则C
	loops = (argc > 1) ? atoi(argv[1]) : 10000000;
	s = pthread_create(&t1,NULL,threadFunc,&loops);//s返回0成功
	if(s != 0)
		printf("s=%d,pthread1_create is failed\n",s);
	s = pthread_create(&t2,NULL,threadFunc,&loops);//s返回0成功
	if(s != 0)
		printf("s=%d,pthread2_create is failed\n",s);
	
	s= pthread_join(t1,NULL);//retval为一非空指针,将会保存线程终止时返回值的拷贝
	if(s != 0)
		printf("s=%d,pthread1_join is failed\n",s);
	s= pthread_join(t2,NULL);//retval为一非空指针,将会保存线程终止时返回值的拷贝
	if(s != 0)
		printf("s=%d,pthread2_join is failed\n",s);
	printf("glob=%d\n",glob);
	return 0;
}

执行结果
在这里插入图片描述
分析如下图
在这里插入图片描述
这一行为的不确定性,实应归咎于内核 CPU 调度决定的难以预见。为避免线程更新共享变量时所出现问题,必须使用互斥量(mutex 是 mutual exclusion 的缩写)来确保同时仅有一个线程可以访问某项共享资源。更为全面的说法是,可以使用互斥量来保证对任意共享资源的原子访问,而保护共享变量是其最常见的用法。

二、使用静态互斥量保护对全局变量的访问

#include <pthread.h>
//#include "tlpi_hdr.h"
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
/**********************************************
互斥量是属于pthread_mutex_t类型的变量。在使用之前必须对其初始化。
对于静态分配的互斥量而言,将PTHREAD_MUTEX_INITIALIZER赋给互斥量
 * *******************************************/
static int glob = 0;
static pthread_mutex_t mxt = PTHREAD_MUTEX_INITIALIZER;
static void * threadFunc(void *arg)
{
	int loops = *((int *)arg);
	int loc,j,s;
	for(j=0;j<loops;j++){
		s = pthread_mutex_lock(&mxt);
		if(s != 0)
			printf("s=%d,pthread_mutex_lock is failed\n",s);
		loc = glob;
		loc++;
		glob = loc;
		s = pthread_mutex_unlock(&mxt);
		if(s != 0)
			printf("s=%d,pthread_mutex_lock is failed\n",s);
	}
    return NULL;
}
int main(int argc,char *argv[])
{
	pthread_t t1,t2;
	int s,loops; 
	//式A?B:C值为:若A为真,则B;若A为假,则C
	loops = (argc > 1) ? atoi(argv[1]) : 10000000;
	s = pthread_create(&t1,NULL,threadFunc,&loops);//s返回0成功
	if(s != 0)
		printf("s=%d,pthread1_create is failed\n",s);
	s = pthread_create(&t2,NULL,threadFunc,&loops);//s返回0成功
	if(s != 0)
		printf("s=%d,pthread2_create is failed\n",s);
	s= pthread_join(t1,NULL);//retval为一非空指针,将会保存线程终止时返回值的拷贝
	if(s != 0)
		printf("s=%d,pthread1_join is failed\n",s);
	s= pthread_join(t2,NULL);//retval为一非空指针,将会保存线程终止时返回值的拷贝
	if(s != 0)
		printf("s=%d,pthread2_join is failed\n",s);
	printf("glob=%d\n",glob);
	return 0;
}

执行结果如下:
在这里插入图片描述
分析如下:
一般情况下,对每一共享资源(可能由多个相关变量组成)会使用不同的互斥量,每一
线程在访问同一资源时将采用如下协议。

  • 针对共享资源锁定互斥量
  • 访问共享资源
  • 对互斥量解锁
    如果多个线程试图执行这一代码块(一个临界区),事实上只有一个线程能够持有该互斥量(其
    他线程将遭到阻塞),即同时只有一个线程能够进入这段代码区域,如下图分析:
    在这里插入图片描述
    另外,线程对互斥量的持有时间应尽可能短,以避免妨碍其他线程的并发执行。这也保证了遭堵塞的其他线程可以很快获取对互斥量的锁定。

三、创建了一个带有错误检查属性(error-checking)的互斥量

int s;
pthread_mutex_t mxt;
pthread_mutexattr_t mtxAttr;
s = pthread_mutexattr_init(&mtxAttr);
if(s != 0)
	printf("s=%d,pthread_mutexattr_init is failed\n",s);
s = pthread_mutexattr_settype(&mtxAttr,PTHREAD_MUTEX_ERRORCHECK);
if(s != 0)
		printf("s=%d,pthread_mutexattr_settype is failed\n",s);
s = pthread_mutex_init(&mxt,&mtxAttr);
if(s != 0)
	printf("s=%d,pthread_mutex_init is failed\n",s);
//只有当互斥量处于未锁定状态,且后续也无任何线程企图锁定它时,将其销毁才是安全的
s = pthread_mutexattr_destroy(&mtxAttr);
if(s != 0)
	printf("s=%d,pthread_mutexattr_destroyis failed\n",s);
/***************************************************************
在如下情况下,必须使用函数 pthread_mutex_init(),而非静态初始化互斥量。
1.动态分配于堆中的互斥量。例如,动态创建针对某一结构的链表,表中每个结构都包
含一个 pthread_mutex_t 类型的字段来存放互斥量,借以保护对该结构的访问。
2.互斥量是在栈中分配的自动变量。
3.初始化经由静态分配,且不使用默认属性的互斥量。
当不再需要经由自动或动态分配的互斥量时,应使用 pthread_mutex_destroy()将其销毁。对于使用PTHREAD_MUTEX_INITIALIZER静态初始化的互斥量,无需调用pthread_mutex_destroy()

互斥量行为论述。
1.同一线程不应对同一互斥量加锁两次。
2.线程不应对不为自己所拥有的互斥量解锁(亦即,尚未锁定互斥量)。
3.线程不应对一尚未锁定的互斥量做解锁动作
上述情况的结果将取决于互斥量类型(type)
*******************************************************************/

在学习中进步,如有错误,请多多批评指正

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeAmmon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值