保证同一时间操作共享资源的线程只有一个。
typedef struct
{
int code;
double balance;
}Account,*pAccount;
//取款金额amt
extern double withdraw(pAccount a, double amt)
{
if(amt<0 || amt> a->balance) return 0.0;
double balance = a->balance;
sleep(1);//模拟银行连接互联网取钱的延时
balance -= amt;
a->balance = balance;
return amt; //成功则返回取款金额
}
//------------------------------------------------------------------------
/*
* account_test.c
* 创建boy和girl线程同时对银行账户进行操作
*/
//头文件
//--------------------------------
#include "account.h"
#include
#include
#include
#include
//--------------------------------
//结构体
//--------------------------------
typedef struct
{
char name[20];
pAccount account;
double amt; //存取款金额
}OperArg;
//--------------------------------
//定义取款操作的线程运行函数
//--------------------------------
void* withdraw_fn(void* arg)
{
OperArg *oa = (OperArg*)arg;
double amt = withdraw(oa->account, oa->amt);
printf("%8s(0x%lx) withdraw %f from account %d\n",\
oa->name, pthread_self(), amt, oa->account->code);
return (void*)0;
}
//--------------------------------
//定义存款操作的线程运行函数
//--------------------------------
void* deposit_fn(void* arg)
{
OperArg *oa = (OperArg*)arg;
double amt = deposit(oa->account, oa->amt);
printf("%8s(0x%lx) deposit %f from account %d\n",\
oa->name, pthread_self(), amt, oa->account->code);
return (void*)0;
}
//--------------------------------
//定义检查银行账户的线程运行函数
//--------------------------------
void* check_fn(void* arg)
{
return (void*)0;
}
//--------------------------------
//--------------------------------
//主函数
//--------------------------------
int main(void)
{
int err;
pthread_t boy, girl;
pAccount a = create_account(100001, 10000);
OperArg o1, o2; //o1,o2分别代表操作的两个用户
strcpy(o1.name, "boy");
o1.account = a;
o1.amt = 10000;
strcpy(o2.name, "girl");
o2.account = a;
o2.amt = 10000;
//启动两个子线程(boy和girl)
//同时操作同一个银行账户
err = pthread_create(&boy, NULL,withdraw_fn, (void*)&o1);
if(err != 0) perror("pthread create error");
err = pthread_create(&girl, NULL,withdraw_fn, (void*)&o2);
if(err != 0) perror("pthread create error");
pthread_join(boy, NULL);
pthread_join(girl, NULL);
printf("account balance: %f\n", get_balance(a));
destroy_account(a);
return 0;
}
输出:
girl(0xb657e460) withdraw 10000.000000 from account 100001
boy(0xb6d7f460) withdraw 10000.000000 from account 100001
account balance: 0.000000
男生女生同时取款,都取到了10000元,相当于两人一共取了20000,这是不对的。
此时线程不安全。
分析:boy girl 两者共同调用了withdraw函数,在栈中都有一个空间,girl在boy延时睡眠时进来,此时账户里余额并未改变,于是两人得到的balance都为10000。
#include
int pthread_mutex_init(
pthread_mutex_t* restrict mutex,
const pthread_mutexattr_t* mutexattr);
int pthread_mutex_destroy(pthread_mutex_t* mutex);
#include
int pthread_mutex_lock(pthread_mutex_t* mutex);
功能:上锁,拿不到锁则阻塞
int pthread_mutex_trylock(pthread_mutex_t* mutex);
功能:上锁,拿不到锁则返回出错信息
int pthread_mutex_unlock(pthread_mutex_t* mutex);
功能:释放锁
返回:成功返回0,否则返回错误编号。
对ATM示例进行加锁
/*account.c
*/
typedef struct
{
int code;
double balance;
//定义一把互斥锁,用来对多个线程操作的银行账户(共享资源)进行加锁(保护)
//建议一把互斥锁绑定一个账户,尽量不设置成全局变量,否则可能出现一把锁去锁几百个银行账户导致并发性降低
pthread_mutex_t mutex;
}Account,*pAccount;
#include "account.h"
#include
#include
#include
#include
//创建账户
extern pAccount create_account(int code, double balance)
{
pAccount a = (pAccount)malloc(sizeof(Account));
assert(a != NULL);
a->code = code;
a->balance = balance;
//对互斥锁进行初始化
pthread_mutex_init(&a->mutex, NULL);
return a;
}
//销毁账户
extern void destroy_account(pAccount a)
{
assert(a != NULL);
//销毁互斥锁
pthread_mutex_destroy(&a->mutex);
free(a);
}
//取款金额amt
extern double withdraw(pAccount a, double amt)
{
assert(a != NULL);
//以下对共享资源从上锁到释放的代码区称为
//对共享资源操作的临界区,犹如一个禁区
//对共享资源(账户)加锁
pthread_mutex_lock(&a->mutex);
if(amt<0 || amt> a->balance)
{
//释放互斥锁
pthread_mutex_unlock(&a->mutex);
return 0.0;
}
double balance = a->balance;
sleep(1);//模拟银行连接互联网取钱的延时
balance -= amt;
a->balance = balance;
//释放互斥锁
pthread_mutex_unlock(&a->mutex);
return amt; //成功则返回取款金额
}
//存款
extern double deposit(pAccount a, double amt)
{
assert(a != NULL);
//对共享资源(账户)加锁
pthread_mutex_lock(&a->mutex);
if(amt<0){
//释放互斥锁
pthread_mutex_unlock(&a->mutex);
return 0.0;
}
double balance = a->balance;
sleep(1);
balance += amt;
a->balance = balance;
//释放互斥锁
pthread_mutex_unlock(&a->mutex);
return amt;
}
//查看账户余额
extern double get_balance(pAccount a)
{
assert(a != NULL);
//对共享资源(账户)加锁
pthread_mutex_lock(&a->mutex);
double balance = a->balance;
//释放互斥锁
pthread_mutex_unlock(&a->mutex);
return balance;
}
给每个线程加上锁,再运行主函数
输出:
boy(0xb6dc5460) withdraw 10000.000000 from account 100001
girl(0xb65c4460) withdraw 0.000000 from account 100001
account balance: 0.000000
#include
int pthread_mutexattr_init(pthread_mutexattr_t* attr);
int pthread_mutexattr_destroy(pthread_mutexattr_t* attr);
#include
int pthread_mutexattr_getpshared(
const pthread_mutexattr_t* restrict attr,
int* restrict pshared);
int pthread_mutexattr_setpshared(
pthread_mutexattr_t* attr,
int pshared);
#include
int pthread_mutexattr_gettype(
const pthread_mutexattr_t* restrict attr,
int* restrict type);
int pthread_mutexattr_settype(
pthread_mutexattr_t* attr,
int type);
示例
/*
* lock_type.c
* 互斥锁类型
*/
#include
#include
#include
#include
int main(int argc, char **argv)
{
pthread_mutex_t mutex;
if(argc<2){
printf("-usage:%s [error|normal|recursive]\n",argv[0]);exit(1);
}
//定义互斥锁属性
pthread_mutexattr_t mutexattr;
//初始化互斥锁属性
pthread_mutexattr_init(&mutexattr);
//check argv传入的互斥锁类型
if(!strcmp(argv[1],"error")){
//设置互斥锁类型
pthread_mutexattr_settype(&mutexattr,\
PTHREAD_MUTEX_ERRORCHECK);
}
else if(!strcmp(argv[1],"normal")){
pthread_mutexattr_settype(&mutexattr,\
PTHREAD_MUTEX_NORMAL);
}
else if(!strcmp(argv[1],"recursive")){
pthread_mutexattr_settype(&mutexattr,\
PTHREAD_MUTEX_RECURSIVE);
}
//按指定类型初始化互斥锁
pthread_mutex_init(&mutex,&mutexattr);
//上锁
if(pthread_mutex_lock(&mutex)!=0)
printf("lock failure\n");
else
printf("lock success\n");
//第二次上锁
if(pthread_mutex_lock(&mutex)!=0)
printf("lock failure\n");
else
printf("lock success\n");
//上两次锁,也需要释放两次
pthread_mutex_unlock(&mutex);
pthread_mutex_unlock(&mutex);
//销毁互斥锁和属性
pthread_mutexattr_destroy(&mutexattr);
pthread_mutex_destroy(&mutex);
return 0;
}
输出:
$ ./lock_type error
lock success
lock failure
$ ./lock_type recursive
lock success
lock success
$ ./lock_type normal
lock success
^C
$
#include
int pthread_rwlock_init(pthread_rwlock_t* restrict rwlock,\
const pthread_rwlockattr_t* restrict attr);
int pthread_rwlock_destroy(pthread_rwlock_t* rwlock);
#include
//功能:加读锁
int pthread_rwlock_rdlock(pthread_rwlock_t* rwlock);
//功能:加写锁
int pthread_rwlock_wrlock(pthread_rwlock_t* rwlock);
//功能:释放锁
int pthread_rwlock_unlock(pthread_rwlock_t* rwlock);
/*
* rwlock_feature.c
*
*/
#include
#include
#include
#include
//定义读写锁
pthread_rwlock_t rwlock;
int main(int argc, char **argv)
{
if(argc<3){
printf("-usage:%s [r|w] [r|w]\n",argv[0]);
exit(1);
}
//读写初始化
pthread_rwlock_init(&rwlock,NULL);
if(!strcmp("r",argv[1])){
//加读锁
if(pthread_rwlock_rdlock(&rwlock) != 0)
{
printf("first read lock failure\n");}
else
{
printf("first read lock success\n");}
}
if(!strcmp("w",argv[1])){
//加写锁
if(pthread_rwlock_wrlock(&rwlock) != 0)
{
printf("first write lock failure\n");}
else
{
printf("first write lock success\n");}
}
if(!strcmp("r",argv[2])){
//加读锁
if(pthread_rwlock_rdlock(&rwlock) != 0)
{
printf("second read lock failure\n");}
else
{
printf("second read lock success\n");}
}
if(!strcmp("w",argv[2])){
//加写锁
if(pthread_rwlock_wrlock(&rwlock) != 0)
{
printf("second write lock failure\n");}
else
{
printf("second write lock success\n");}
}
//释放读写锁
pthread_rwlock_unlock(&rwlock);
pthread_rwlock_unlock(&rwlock);
return 0;
}
输出:
$ ./rwlock_feature r r
first read lock success
second read lock success
$ ./rwlock_feature r w
first read lock success
^C
$ ./rwlock_feature w w
first write lock success
second write lock failure
$ ./rwlock_feature w r
first write lock success
second read lock failure
分析: