并发中,解决数据同步的方法
方法一:原子操作
把a++变成原子操作,要么不执行,要么一口气执行完
实现原子操作无法依赖编译器,需要我们自己动手,x86有很多原子指令,我们只需要直接应用这些指令,用汇编代码写出对应的原子操作函数就可,而现代C语言已经支持嵌入汇编代码,所以在C函数中可以按照特定的方式嵌入汇编代码,实现原子操作
//定义一个原子类型
typedef struct s_ATOMIC{
volatile s32_t a_count;
}atomic_t;
//原子读
static inline s32_t atomic_read(const atomic_t *v)
{
return (*(volatile u32_t*)&(v)->a_count);
}
//原子写
static inline void atomic_write(atomic_t *v,int i)
{
v->a_count = i;
}
//原子加上一个整数
static inline void atomic_add(int i,atomic_t *v)
{
__asm__ __volatile__("lock;" "addl %1,%0"
:"+m"(v->a_count)
:"ir"(i));
}
//原子减去一个整数
static inline void atomic_sub(int i,atomic_t *v)
{
__asm__ __volatile__("lock;" "subl %1,%0"
:"+m"(v->a_count)
:"ir"(i));
}
//原子加1
static inline void atomic_inc(atomic_t *v)
{
__asm__ __volatile__("lock;" "incl%0"
:"+m"(v->a_count));
}
//原子减1
static inline void atomic_dec(atomic_t *v)
{
__asm__ __volatile__("lock;" "decl %0"
:"+m"(v->a_count));
}
以上代码中,加上lock前缀的addl,subl,incl,decl指令都是原子操作,lock前缀表示锁定总线
GCC设计了一种特有的嵌入方式,规定了汇编代码嵌入的形式和嵌入汇编代码需要哪几部分
__asm__ __volatile__(代码部分:输出部分列表:输入部分列表:损坏部分列表);
括号里大致分为4部分:
1.汇编代码部分,实际嵌入的汇编代码
2.输出列表部分,让GCC能够处理C语言左值表达式与汇编代码的结合
3.输入列表部分,让GCC能够处理C语言表达式、变量、常量,让它们能够输入到汇编代码中
4.损坏列表部分,告诉GCC汇编代码中用到了哪些寄存器,以便GCC在汇编代码运行前,生成保存他们的代码
方法二:中断控制 搞定复杂变量
方法三:自旋锁 协调多核心CPU
自旋锁原理,首先读取锁变量,判断其值是否已经加锁,如果未加锁则执行加锁,然后返回,表示加锁成功;如果已经加锁,就要返回第一部继续执行后续步骤
要正确的执行它,就必须保证读取锁变量和判断并加锁的操作是原子执行的
x86有一个原子交换指令,xchg,它可以让寄存器里的一个值跟内存空间中的一个值做交换
方法四:信号量CPU时间管理
等待,互斥,唤醒
信号量的使用步骤
一、获取信号量
1.首先开启自旋锁保护信号量
2.对信号量计数值执行“加一”操作,并检查是否大于0
3.小于0,让进程进入等待状态并且将其挂入wait中,然后调度其他进程运行。否则表示信号量获取成功,然后解锁
二、代码执行流开始执行相关操作,入读取键盘缓冲区
三、释放信号量
1.加自旋锁保护信号量
2.对信号量计数值“加一”,并检查是否大于零
3.大于0唤醒wait中的进程