本文主要是对线程的互斥部分做简单的笔记,记录实现线程互斥的基本代码。
线程互斥
线程互斥,指的是线程执行的相互排斥。
解决互斥的方法:
- 互斥锁
- 读写锁
- 线程信号量
线程同步
线程同步,指的是多个线程执行的互斥与先后的顺序。
解决同步的方式:
- 条件变量
- 线程信号量
实例代码,以银行取款为例,先看一个线程共享数据,数据不安全的实例:
account.h
#ifndef ACCOUNT_H
#define ACCOUNT_H
#endif
typedef struct {
int code;
double banlance;
}Account;
extern Account* create_account(int code, double banlance);
extern void free_account(Account* ant);
extern double deposit(Account* ant, double amt);
extern double withdrawal(Account* ant, double amt);
extern double get_banlance(Account* ant);
account.c
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "account.h"
Account* create_account(int code, double banlance) {
assert(banlance != 0);
assert(code != 0);
Account* ant = (Account*)malloc(sizeof(Account));
if (ant == NULL) {
perror("malloc error");
exit(1);
}
ant->code = code;
ant->banlance = banlance;
return ant;
}
void free_account(Account* ant) {
free(ant);
}
double deposit(Account* ant, double amt) {
assert(ant != NULL);
if (amt <= 0) {
return 0.0;
}
double cur_banlance = ant->banlance;
sleep(1);
cur_banlance += amt;
ant->banlance = cur_banlance;
return amt;
}
double withdrawal(Account* ant, double amt) {
assert(ant != NULL);
if (amt <= 0) {
return 0.0;
}
double cur_banlance = ant->banlance;
if (cur_banlance < amt) {
return 0.0;
}
sleep(1);
cur_banlance -= amt;
ant->banlance = cur_banlance;
return amt;
}
double get_banlance(Account* ant) {
assert(ant != NULL);
return ant->banlance;
}
main.c
#include <pthread.h>
#include <stdio.h>
#include "account.h"
typedef struct {
int number;
int amt;
Account* account;
}OperArgs;
void* withdraw_fn(void* args) {
OperArgs* oa = (OperArgs*)args;
double amt = withdrawal(oa->account, oa->amt);
printf("thread %d: id is: %lu withdrawal %lf from account %d\n",
oa->number,
pthread_self(), amt, oa->account->code);
return (void*)0;
}
int main(int argc, char const *argv[])
{
Account* ant_one = create_account(100, 10000.0);
printf("from the beginning %lf\n", get_banlance(ant_one));
OperArgs o1;
o1.account = ant_one;
o1.amt = 10000;
o1.number = 1;
OperArgs o2;
o2.account = ant_one;
o2.amt = 10000;
o2.number = 2;
pthread_t boy, girl;
pthread_create(&boy, NULL, withdraw_fn, (void*)&o1);
pthread_create(&girl, NULL, withdraw_fn, (void*)&o2);
pthread_join(boy, NULL);
pthread_join(girl, NULL);
double cur_banlance = get_banlance(ant_one);
printf("account number: %d cur_banlance is %lf\n", ant_one->code, cur_banlance);
free_account(ant_one);
}
运行结果如下:
from the beginning 10000.000000
thread 2: id is: 123145571397632 withdrawal 10000.000000 from account 100
thread 1: id is: 123145570861056 withdrawal 10000.000000 from account 100
account number: 100 cur_banlance is 0.000000
这段代码是一个线程不安全的代码,boy和girl两个线程都会去取钱,而且都取钱成功,需要改进,这里使用互斥锁进行改进。
互斥锁
互斥锁mutex是一种简单的加锁方法,用来控制对共享资源的访问。
同一时刻只能有一个线程掌握互斥锁,拥有上锁资源的线程才能对共享资源进行访问。
其他想要访问共享资源的线程会被挂起,直到互斥锁释放。
互斥锁的数据类型为
pthread_mutex_t
定义如下:
typedef union
{
struct __pthread_mutex_s __data;
char __size[__SIZEOF_PTHREAD_MUTEX_T];
long int __align;
} pthread_mutex_t;
为账号加锁,用来解决线程不安全问题
account.h
#ifndef ACCOUNT_H
#define ACCOUNT_H
#endif
typedef struct {
int code;
double banlance;
// 互斥锁加在一个账户上,而不是全局变量
// 不然会导致并发性能降低
pthread_mutex_t lock;
}Account;
extern Account* create_account(int code, double banlance);
extern void free_account(Account* ant);
extern double deposit(Account* ant, double amt);
extern double withdrawal(Account* ant, double amt);
extern double get_banlance(Account* ant);
account.c
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "account.h"
Account* create_account(int code, double banlance) {
assert(banlance != 0);
assert(code != 0);
Account* ant = (Account*)malloc(sizeof(Account));
if (ant == NULL) {
perror("malloc error");
exit(1);
}
ant->code = code;
ant->banlance = banlance;
//初始化互斥锁
pthread_mutex_init(&ant->lock, NULL);
return ant;
}
void free_account(Account* ant) {
assert(ant != NULL);
// 销毁互斥锁
pthread_mutex_destroy(&ant->lock);
free(ant);
}
double deposit(Account* ant, double amt) {
assert(ant != NULL);
// 对共享资源加锁
pthread_mutex_lock(&ant->lock);
if (amt <= 0) {
// 释放锁
pthread_mutex_unlock(&ant->lock);
return 0.0;
}
double cur_banlance = ant->banlance;
sleep(1);
cur_banlance += amt;
ant->banlance = cur_banlance;
// 释放锁
pthread_mutex_unlock(&ant->lock);
return amt;
}
double withdrawal(Account* ant, double amt) {
assert(ant != NULL);
// 对共享资源加锁
pthread_mutex_lock(&ant->lock);
if (amt <= 0) {
// 释放锁
pthread_mutex_unlock(&ant->lock);
return 0.0;
}
double cur_banlance = ant->banlance;
if (cur_banlance < amt) {
return 0.0;
}
sleep(1);
cur_banlance -= amt;
ant->banlance = cur_banlance;
// 释放锁
pthread_mutex_unlock(&ant->lock);
return amt;
}
double get_banlance(Account* ant) {
assert(ant != NULL);
return ant->banlance;
}
main.c:
#include <pthread.h>
#include <stdio.h>
#include "account.h"
typedef struct {
int number;
int amt;
Account* account;
}OperArgs;
void* withdraw_fn(void* args) {
OperArgs* oa = (OperArgs*)args;
double amt = withdrawal(oa->account, oa->amt);
printf("thread %d: id is: %lu withdrawal %lf from account %d\n",
oa->number,
pthread_self(), amt, oa->account->code);
return (void*)0;
}
int main(int argc, char const *argv[])
{
Account* ant_one = create_account(100, 10000.0);
printf("from the beginning %lf\n", get_banlance(ant_one));
OperArgs o1;
o1.account = ant_one;
o1.amt = 10000;
o1.number = 1;
OperArgs o2;
o2.account = ant_one;
o2.amt = 10000;
o2.number = 2;
pthread_t boy, girl;
pthread_create(&boy, NULL, withdraw_fn, (void*)&o1);
pthread_create(&girl, NULL, withdraw_fn, (void*)&o2);
pthread_join(boy, NULL);
pthread_join(girl, NULL);
double cur_banlance = get_banlance(ant_one);
printf("account number: %d cur_banlance is %lf\n", ant_one->code, cur_banlance);
free_account(ant_one);
}
运行结果:
from the beginning 10000.000000
thread 1: id is: 123145474289664 withdrawal 10000.000000 from account 100
thread 2: id is: 123145474826240 withdrawal 0.000000 from account 100
account number: 100 cur_banlance is 0.000000