多线程竞争锁时避免饿死

A线程平均每0.4ms发起一次lock(), 有时候要处理0.4ms以上才能unlock(), 此时A线程刚unlock()就会进入下一次lock(), B线程要跟A线程抢锁,情况差的时候要等几十ms才能拿到。
目前找到两种解决方法

  1. A线程每次unlock()之后,sleep(0), 相当于主动给其他线程一个机会

暂时放弃cpu,也就是释放一些未用的时间片给其他线程或进程使用,就相当于一个让位动作。
当线程调用sleep(n)的时候,线程是由运行态转入等待态,线程被放入等待队列中,等待定时器n秒后的中断事件,线程才重新由等待态转入就绪态,被放入就绪队列中,等待队列中的线程是不参与cpu竞争的,只有就绪队列中的线程才会参与cpu竞争。
而sleep(0)之所以马上回去参与cpu竞争,是因为调用sleep(0)后,线程直接回到就绪队列。

每个Mutex都有一个等待队列,一个线程在mutex上挂起等待时, 首先把自己加入等待队列中,然后致线程状态为睡眠,然后调用OS的调度函数切换到别的线程。
unlock后,mutex等待队列中的线程被唤起,它的状态从等待态(=睡眠、挂起?)改为就绪态,被放入就绪队列。

如果不sleep(0),A线程unlock()时B线程被加到就绪队列,但是A继续运行,又拿到锁,没有给B线程lock()的机会,调度算法让B运行时,B lock(), 再次回到等待队列;
通过sleep(0), A线程放到就绪队列的队尾,B在A的前面,所以B先运行,可以拿到锁。

  1. 使用两个mutex
  • data mutex (‘M’)
  • next-to-access mutex (‘N’)
void FIFOlock(){
    N.lock();
    M.lock();
    N.unlock();
}
void FIFOunlock(){
    M.unlock();
}

N的作用是确保任一时刻最多只有一个线程能挂在M上,先执行FIFOlock()的线程拿到N,后执行FIFOlock()的挂在N上,因此FIFOunlock()时,一定只有第一个来的线程可以拿到M,然后释放N,让所有挂在N上的随机挑一个拿到N,开始挂在M上。这样FIFOlock()具有半FIFO的性质,只保证第一个进的第一个出,后面的不保证。

拓展:使用三个mutex实现优先锁,参见https://stackoverflow.com/a/11673600

  • data mutex (‘M’)
  • next-to-access mutex (‘N’)
  • low-priority access mutex (‘L’)

这个半FIFO队列里,算上正在运行的线程,至多只能有一个低优先级线程。

void lowpriolock(){
  L.lock();
  FIFOlock();
}

void lowpriounlock(){
  FIFOunlock();
  L.unlock();
}

void highpriolock(){
  FIFOlock();
}

void highpriounlock(){
  FIFOunlock();
}

但是不太对,低优先级线程在半FIFO队列里还是有可能排在高优先级的前面。
是否有可能只靠mutex的lock,unlock, 顶多加上try_lock, 实现“有高优先级在等时,一定不会让低优先级的先上”?
一个用了额外int变量的方案:

atomic_int hpc(0);
mutex H,L;
void lowpriolock(){
    L.lock();
}
void highpriolock(){
    hpc++;
    H.lock();
    hpc--;
}
void unlock(){
    if(hpc){
        H.unlock();
    }else{
        L.unlock();
    }
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值