c语言线程锁的原理开锁原理图,多线程锁原理

多线程锁原理

临界区: 在临界区内,会对共享资源进行访问和修改

共享资源: 在同一时间只能被单个线程所持有

访问临界区过程:

申请临界区权限

访问临界区

归还权限, 退出临界区

线程安全问题:

12306卖票问题,既不能多卖又不能少卖

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

如果多个线程,同时操作的话,可能会造成多卖票,本来只有1000张票,却卖个1001个人

为了避免这种情况,可以尝试使用标志位来解决

var isSaling = false // 正在售票标志位

while isSaling { // 如果有人售票,则等待

}

isSaling = true // 表示我正在售票,你们先等等

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

isSaling =false // 卖完票了,你们可以操作了

遗憾的是这种方式,仍然无法解决安全问题,关键在于查询操作和修改操作并非不可中断,造成多个程序进入临界区:

Thread 1

Thread 2

query isSaling = false

query isSaling = false

set isSaling = true

set isSaling = true

卖票

卖票

为了防止这种情况发生。于是决定设置两个标志位

var isSaling1 = false // 正在售票标志位

var isSaling2 = false // 正在售票标志位

// =====================

// thread 1

while isSaling2 { // 2号线程正在售票,稍微等等

}

isSaling1 = true // 表示我正在售票,你们先等等

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

isSaling1 = false // 卖完票了,你们可以操作了

// thread 2

// =================================

while isSaling1 { // 1号线程正在售票,稍微等等

}

isSaling2 = true // 表示我正在售票,你们先等等

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

isSaling2 = false // 卖完票了,你们可以操作了

这种思维方式就像,有一条公家的小板凳,我先看下隔壁有没有占用那条小板凳,没有的话我就拿过来坐坐

但是仍然会发生多个线程进入临界区的问题: ** isSaling1 = false, isSaling2 = false, thread1 exec to while isSaling2 expr and thread2 exec to while isSaling1 expr **

可见光增加flag无法解决问题,换一种方式增加临界区大小

var isSaling1 = false // 正在售票标志位

var isSaling2 = false // 正在售票标志位

// =====================

// thread 1

isSaling1 = true // 表示我正在售票,你们先等等

while isSaling2 { // 2号线程正在售票,稍微等等

}

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

isSaling1 = false // 卖完票了,你们可以操作了

// thread 2

// =================================

isSaling2 = true // 表示我正在售票,你们先等等

while isSaling1 { // 1号线程正在售票,稍微等等

}

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

isSaling2 = false // 卖完票了,你们可以操作了

这种方式却会造成死锁,死锁的时机和之前一样。** isSaling1 = false, isSaling2 = false, thread1 exec to while isSaling2 expr and thread2 exec to while isSaling1 expr **, 只不过一个是造成多个线程共同访问临界区,一个是造成死锁

为了解决这个问题,产生了软件上的解法和硬件上的解法,

硬件的解法即源语。

软件上:

这两种算法思想其实是一致的,就是当线程进入临界区之后,发现别的线程也进入了临界区,怎么办?如何避免死锁或者并非, 这个时候就需要制定一个规则让他们能够正确运行

** Dekker 算法

var isSaling1 = false // 正在售票标志位

var isSaling2 = false // 正在售票标志位

var turn = 1

// =====================

// thread 1

isSaling1 = true // 表示我正在售票,你们先等等

while isSaling2 { // 这个时候发现2号正在售票

if turn == 2 {

isSaling1 = fasle // 我不卖了,让2号先卖

while(isSaling2) {} // 等待2号卖票

isSaling1 = true // 2号已经卖完票了轮到我卖了

}

}

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

isSaling1 = false // 卖完票了,你们可以操作了

turn = 2

// thread 2

// =================================

isSaling2 = true // 表示我正在售票,你们先等等

while isSaling1 { // 1号线程正在售票,稍微等等

if turn == 1 {

isSaling2 = fasle

while(isSaling1) {}

isSaling2 = true

}

}

if more_ticket > 0 {

// 卖票

more_ticket -= 1

}

turn = 1

isSaling2 = false // 卖完票了,你们可以操作了

这里turn = 1时,发生碰撞1号优先卖票,等于2时则相反,双方不断的切换优先级,防止死锁的同时避免多线程并发进入临界区

** Peterson 算法

var inside:array[1..2] of boolean;

turn:integer;

turn := 1 or 2;

inside[1] := false;

inside[2] := false;

cobegin

process P1

begin

/* P1 不在其临界区内*/ /* P2 不在其临界区内*/

inside[1]:= true;

turn := 2;

while (inside[2] and turn=2)

do begin end;

临界区;

inside[1] := false;

end;

process P2

begin

inside[2] := true;

turn := 1;

while (inside[1] and turn=1)

do begin end;

临界区;

inside[2] := false;

end;

coend.

这个算法的规则更加简化,由于两个线程都会修改turn的值,那么最后只有一个线程执行,另外一个堵塞,画个表看下会更加清晰

Thread 1

Thread 2

inside[1]:= true

inside[2] := true

turn := 2

if inside[2]

if turn := 2

// 线程1 被堵塞

turn := 1

// 线程1 执行

while (inside[1] and turn=1) 条件为真,线程被堵塞

inside[1] := false

线程2 执行

这个算法还是很精髓的

硬件指令

硬件指令相对于软件算法的不同在于不可分割性,也就是不会被中断,操作一起呵成

Test And Set 指令

TS(x): 若x=true,则x:=false;return true;否则return false;

s : boolean;

s := true;

process Pi /* i = 1,2,...,n*/

pi : boolean;

begin

repeat pi := TS(s) until pi; /*上锁*/ 临界区;

s := true; /*开锁*/

end;

关键句子: pi := TS(s)

// s = true

// =============

p0 = TS(s)

// 执行之后

// s = false , p0 = true ,线程不会被阻塞

// ==========

// 由于TS指令的特殊性,导致执行的时候必然会有先后顺序,哪怕两个线程是同时调用的该指令,这里假定p0线程在前,p1在后

// 此时 s = false

p1 = TS(s)

// 执行之后

// p1 = false , s = false , set 失败, 线程被阻塞,直到p0 设置 s = true

Swap 指令

Swap (a,b): temp:=a; a:=b; b:=temp;

lock : boolean;

lock := false;

process Pi

pi : boolean;

begin

pi := true;

/* i = 1,2,...,n*/

repeat Swap(lock, pi) until pi = false;/*上锁*/ 临界区;

lock := false; /*开锁*/

end;

原理和上面的类似第一次交换:

// lock = false

// p0 = true

swap(p0,lock)

// p0 = false , lock = true

第二次交换

// lock = true

// p1 = true

swap(p0,lock)

// p1 = true , lock = true, 即swap(true,true)

线程被阻塞

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值