Linux线程浅析[线程的同步和互斥]
- 线程同步
- 线程互斥
- 线程互斥的相关函数
- 线程同步的相关函数
线程同步
是宏观上的一个概念,在微观上面包含线程的相互排斥和线程的执行顺序的约束问题
解决方法:
条件变量
线程信号量
例子:一栋大楼的建造,包含了质监局的检验和工程队的施工,那么假设质监局和工程队是两个线程,而大楼则为共享资源,首先相互排斥的原则为工程队施工的时候,质监局是不能进行检验的,因为这个时候大楼还没有建好,而质监局的检验依赖于工程队施工的结果,所以质监局必须等待工程队将大楼建造好.所以后一个线程依赖于前一个线程对共享资源操作的结果.所以说线程同步是包含了相互排斥和线程的执行顺序.
线程互斥
线程的相互排斥
解决方法:
互斥锁
读写锁
线程信号量
例子:
一个公用厕所,假设只有一个坑,那么很多人去上厕所的时候,后一个就必须等后一个上完厕所之后才能上厕所.所以同一时间只能有一个人上厕所.这就是互斥.其实在生活中有很多例子.如银行账户的操作问题.12306的售票问题等.都是互斥
注意:同步包含了互斥,互斥仅仅只是相互排斥,而同步是互斥加顺序
先来一段有问题的代码:两个线程同时去访问同一个账户的问题:
account.h
/*
* ===========================================================================
*
* Filename: account.h
* Description:
* Version: 1.0
* Created: 2017年03月28日 22时04分18秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#ifndef __ACCOUNT_H_
#define __ACCOUNT_H_
//声明银行账户的结构体
typedef struct{
int code;
double balance;
}Account;
//创建银行账户
extern Account* create_account(int code,double balance);
//销毁一个账户
extern void destory_account(Account* account);
//存款
extern double withdraw(Account* account,double amt);
//取款
extern double deposit(Account* a,double amt);
//查看账户余额
extern double get_balance(Account* account);
#endif
account.c
/*
* ===========================================================================
*
* Filename: account.c
* Description:
* Version: 1.0
* Created: 2017年03月30日 21时16分28秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>
/*
*创建一个账户
*返回这个账户的指针
*
* */
extern Account* create_account(int code,double balance){
Account* account = (Account*)malloc(sizeof(Account));
assert(account !=NULL);
account->code = code;
account->balance = balance;
//初始化锁的过程
return account;
}
/* *
*销毁一个账户
* */
extern void destory_account(Account* account){
assert(account!=NULL);
free(account);
}
/* *
*取款,返回的是账户的余额
*
* */
extern double withdraw(Account * account,double amt){
assert(account != NULL);
if(amt < 0 || amt > account->balance){
return 0.0;
}
int balance = account -> balance;
sleep(1);
int result = balance - amt;
account ->balance = result;
//pthread_mutex_unlock(&account->pthreadmutex);
return amt;
}
/* *
*存款,返回的也是账户的余额
*
* */
extern double deposit(Account* account,double amt){
assert(account != NULL);
if(amt < 0){
return 0.0;
}
int result = account ->balance + amt;
sleep(2);
account->balance = result;
return result;
}
/* *
*获取的就是这个账户的余额
*
* */
extern double get_balance(Account* account){
assert(account !=NULL);
return account -> balance;
}
accountTest.c
/*
* ===========================================================================
*
* Filename: accountTest.c
* Description:
* Version: 1.0
* Created: 2017年03月30日 21时31分19秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include"account.h"
typedef struct{
char name[20];
Account* account;
double amt;
}ArgvType;
/* *
*检查账户的函数
* */
void* th_check(void *argv){
ArgvType* argvType = (void*)argv;
double balance = get_balance(argvType->account);
printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
return (void*)0;
}
/* *
*取款函数
* */
void* th_withdraw(void *argv){
ArgvType* argvType = (ArgvType*)argv;
double balance = withdraw(argvType->account,argvType->amt);
printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
return (void*)0;
}
/* *
*存款函数
* */
void* th_deposite(void *argv){
ArgvType* argvType = (ArgvType*)argv;
double balance = deposit(argvType->account,argvType->amt);
printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
return (void*)0;
}
int main(int argc,char *argv[]){
int result;
pthread_t boyfriend,girlfriend;
Account *account = create_account(10001,10000);
ArgvType argv_typeboy,argv_typegirl;
strcpy(argv_typeboy.name,"zhangsan");
strcpy(argv_typegirl.name,"xiaoli");
argv_typeboy.account = account;
argv_typegirl.account = account;
argv_typeboy.amt = 10000;
argv_typegirl.amt = 10000;
if((result = pthread_create(&boyfriend,NULL,th_withdraw,(void*)&argv_typeboy))!=0){
perror("boythread create error");
exit(EXIT_FAILURE);
}
if((result = pthread_create(&girlfriend,NULL,th_withdraw,(void*)&argv_typegirl))!=0){
perror("girlthread create error");
exit(EXIT_FAILURE);
}
pthread_join(boyfriend,NULL);
pthread_join(girlfriend,NULL);
printf("get_balance:%f\n",get_balance(account));
printf("thread:%lx exe ended\n",pthread_self());
return 0;
}
name:zhangsan,withdraw:10000.000000,balance:0.000000,pthread:7f34bf2a9700
name:xiaoli,withdraw:10000.000000,balance:0.000000,pthread:7f34beaa8700
get_balance:0.000000
thread:7f34bfa94740 exe ended
输出的结果如下所示:
从上面输出结果中我们可以看到,其实xiaoli和zhangsan都取到了10000,但是这中情况是及其不符合日常生活的逻辑的,而我们的线程互斥的目的也就是为了解决这样一种并发,即多个线程访问同一个共享资源的时候,怎么去进行处理,后面在此例子基础上面去做修改.
线程互斥的相关函数
互斥锁:
互斥锁是一种简单的加锁的方法来控制对共享资源的访问。在同一时刻只能有一个线程掌握某个互斥锁,拥有上锁状态的线程能够对共享资源的访问,若其他线程希望上锁一个已经被上了锁的资源,则该线程挂起,知道上锁的线程释放互斥锁为止;
互斥锁数据类型:pthread_mutex_t
#include<pthread.h>
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *mutexattr);//初始化互斥锁
int pthread_mutex_destory(pthread_mutex_t * mutex);
//销毁互斥锁
返回:成功返回0,失败返回错误编号
参数:
mutex:互斥锁
mutexattr:互斥锁创建方式
PTHREAD_MUTEX_INITIALIZER:创建快速互斥锁
PTHREAD_RECERSIVE_MUTEX_INITIALIZER_NP:创建递归互斥锁
PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP:创建检错互斥锁
互斥锁的加锁和释放锁的过程
#include<pthread.h>
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,错误返回错误码
参数:mutex
一个共享资源绑定一把锁,
建议:定义在结构体中,尽量不设置成为全局变量,否则可能会出现一把锁去锁几百个账户,导致并发性的下降:
临界区:对共享资源进行加锁到释放锁的整个代码区域(从上锁开始到释放锁结束)
互斥锁的属性创建和销毁;
#include<pthread.h>
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_destory(pthead_muteattr_t *attr);
返回值:成功返回0,失败返回错误码
参数:
attr:互斥锁属性
互斥锁的进程共享属性:
#include<pthread.h>
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *restrict attr,int *restrict pshared)
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr,int pshared)
返回:成功返回0,失败返回错误编号
参数:
attr:互斥锁属性
pshared:进程共享属性
PTHREAD_PROCESS_PRIVATE(默认)
锁只能用于一个进程内部的两个线程进行互斥
PTHREAD_PROCESS_SHARED
锁能够用于两个不同进程中的线程进行互斥(跨进程)
一般情况下我们都是在同一个进程中去使用互斥锁
互斥锁的类型:
#include<pthread.h>
int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr,int *restrict type)
int pthread_mutexattr_settype(pthread_amutexattr_t *attr,int type)
参数
attr 互斥锁属性
type:互斥锁类型
标准互斥锁:PTHREAD_MUTEX_NOMAL
第一次上锁成功,第二次上锁失败
递归互斥锁:PTHREAD_MUTEX_RECURSIVE
第一次上锁成功,第二次上锁还是成功,内部计数
检错互斥锁:PTHREAD_MUTEX_ERRORCHECK
第一次上锁成功,第二次上错会出错
默认互斥锁:PTHREAD_MUTEX_DEFAULT(同标准互斥锁)
先上一个简单的上锁的案例:,在上述基础上修改的
/*
* ===========================================================================
*
* Filename: account.h
* Description:
* Version: 1.0
* Created: 2017年03月28日 22时04分18秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#ifndef __ACCOUNT_H_
#define __ACCOUNT_H_
//声明银行账户的结构体
//引用pthread头文件.方便后面加锁
#include<pthread.h>
typedef struct{
int code;
double balance;
//将锁声明在结构体中,有助于每一个这样的结构体都有一把锁,那么在操作的时候,有利于优化并发效果
pthread_mutex_t pthreadmutex;
}Account;
//创建银行账户
extern Account* create_account(int code,double balance);
//销毁一个账户
extern void destory_account(Account* account);
//存款
extern double withdraw(Account* account,double amt);
//取款
extern double deposit(Account* a,double amt);
//查看账户余额
extern double get_balance(Account* account);
#endif
/*
* ===========================================================================
*
* Filename: account.c
* Description:
* Version: 1.0
* Created: 2017年03月30日 21时16分28秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>
/*
*创建一个账户
*返回这个账户的指针
*
* */
extern Account* create_account(int code,double balance){
Account* account = (Account*)malloc(sizeof(Account));
assert(account !=NULL);
account->code = code;
account->balance = balance;
//初始化锁的过程
pthread_mutex_init(&account->pthreadmutex,NULL);
return account;
}
/* *
*销毁一个账户
* */
extern void destory_account(Account* account){
assert(account!=NULL);
free(account);
//销毁锁的过程
pthread_mutex_destroy(&account->pthreadmutex);
}
/* *
*取款,返回的是账户的余额
*
* */
extern double withdraw(Account * account,double amt){
assert(account != NULL);
//上锁的过程
pthread_mutex_lock(&account->pthreadmutex);
if(amt < 0 || amt > account->balance){
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return 0.0;
}
int balance = account -> balance;
sleep(1);
int result = balance - amt;
account ->balance = result;
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return amt;
}
/* *
*存款,返回的也是账户的余额
*
* */
extern double deposit(Account* account,double amt){
assert(account != NULL);
//上锁的过程
pthread_mutex_lock(&account->pthreadmutex);
if(amt < 0){
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return 0.0;
}
int balance = account ->balance;
sleep(2);
int result = balance + amt;
account->balance = result;
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return result;
}
/* *
*获取的就是这个账户的余额
*
* */
extern double get_balance(Account* account){
assert(account !=NULL);
return account -> balance;
}
/*
* ===========================================================================
*
* Filename: accountTest.c
* Description:
* Version: 1.0
* Created: 2017年03月30日 21时31分19秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<pthread.h>
#include"account.h"
typedef struct{
char name[20];
Account* account;
double amt;
}ArgvType;
/* *
*检查账户的函数
* */
void* th_check(void *argv){
ArgvType* argvType = (void*)argv;
double balance = get_balance(argvType->account);
printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
return (void*)0;
}
/* *
*取款函数
* */
void* th_withdraw(void *argv){
ArgvType* argvType = (ArgvType*)argv;
double balance = withdraw(argvType->account,argvType->amt);
printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
return (void*)0;
}
/* *
*存款函数
* */
void* th_deposite(void *argv){
ArgvType* argvType = (ArgvType*)argv;
double balance = deposit(argvType->account,argvType->amt);
printf("name:%s,balance:%f,pthread:%lx\n",argvType->name,balance,pthread_self());
return (void*)0;
}
int main(int argc,char *argv[]){
int result;
pthread_t boyfriend,girlfriend;
Account *account = create_account(10001,10000);
ArgvType argv_typeboy,argv_typegirl;
strcpy(argv_typeboy.name,"zhangsan");
strcpy(argv_typegirl.name,"xiaoli");
argv_typeboy.account = account;
argv_typegirl.account = account;
argv_typeboy.amt = 10000;
argv_typegirl.amt = 10000;
if((result = pthread_create(&boyfriend,NULL,th_withdraw,(void*)&argv_typeboy))!=0){
perror("boythread create error");
exit(EXIT_FAILURE);
}
if((result = pthread_create(&girlfriend,NULL,th_withdraw,(void*)&argv_typegirl))!=0){
perror("girlthread create error");
exit(EXIT_FAILURE);
}
pthread_join(boyfriend,NULL);
pthread_join(girlfriend,NULL);
printf("get_balance:%f\n",get_balance(account));
printf("thread:%lx exe ended\n",pthread_self());
return 0;
}
输出的结果如上所示,这个时候其实取款的时候就已经没有并发情况再出现了
/*
* ===========================================================================
*
* Filename: account.c
* Description:
* Version: 1.0
* Created: 2017年03月30日 21时16分28秒
* Revision: none
* Compiler: gcc
* Author: (),
* Company:
*
* ===========================================================================
*/
#include"account.h"
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<malloc.h>
#include<assert.h>
#include<pthread.h>
static pthread_mutexattr_t attr;
/*
*创建一个账户
*返回这个账户的指针
*
* */
extern Account* create_account(int code,double balance){
Account* account = (Account*)malloc(sizeof(Account));
assert(account !=NULL);
account->code = code;
account->balance = balance;
//初始化锁的过程
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_NORMAL);
//pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_RECURSIVE);
//pthread_mutexattr_settype(&attr,PTHREAD_MUTEX_ERRORCHECK);
//pthread_mutex_init(&account->pthreadmutex,NULL);
pthread_mutex_init(&account->pthreadmutex,&attr);
return account;
}
/* *
*销毁一个账户
* */
extern void destory_account(Account* account){
assert(account!=NULL);
free(account);
//销毁锁的过程
pthread_mutexattr_destroy(&attr);
pthread_mutex_destroy(&account->pthreadmutex);
}
/* *
*取款,返回的是账户的余额
*
* */
extern double withdraw(Account * account,double amt){
assert(account != NULL);
//上锁的过程
pthread_mutex_lock(&account->pthreadmutex);
if(amt < 0 || amt > account->balance){
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return 0.0;
}
int balance = account -> balance;
sleep(1);
int result = balance - amt;
account ->balance = result;
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return amt;
}
/* *
*存款,返回的也是账户的余额
*
* */
extern double deposit(Account* account,double amt){
assert(account != NULL);
//上锁的过程
pthread_mutex_lock(&account->pthreadmutex);
if(amt < 0){
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return 0.0;
}
int balance = account ->balance;
sleep(2);
int result = balance + amt;
account->balance = result;
//解锁的过程
pthread_mutex_unlock(&account->pthreadmutex);
return result;
}
/* *
*获取的就是这个账户的余额
*
* */
extern double get_balance(Account* account){
assert(account !=NULL);
return account -> balance;
}
以上都是对c的pthread互斥锁做的一个相对比较简单的一些使用方法.有兴趣的可以copy下来run一下,因为我也是新手,写的不好的地方欢迎指出