个性化阅读
专注于IT技术分析

进程和线程:死锁,饥饿和活动锁

先决条件–僵局和饥饿

活锁:当两个或多个进程在不做任何有用的工作的情况下响应其他进程的更改连续重复相同的交互时, 就会发生这种情况。这些进程不处于等待状态, 它们正在同时运行。这与死锁不同, 因为在死锁中, 所有进程都处于等待状态。

死锁,饥饿和活动锁1

例子:

想象一下使用两个资源的一对流程, 如下所示:

void process_A( void )
{
     enter_reg(& resource_1);
     enter_reg(& resource_2);
     use_both_resources();
     leave_reg(& resource_2);
     leave_reg(& resource_1);
}
void process_B( void )
{
     enter_reg(& resource_1);
     enter_reg(& resource_2);
     use_both_resources();
     leave_reg(& resource_2);
     leave_reg(& resource_1);
}

这两个进程中的每个进程都需要两个资源, 并且它们使用轮询原语enter_reg来尝试获取它们所需的锁。万一尝试失败, 该过程将再次尝试。

如果进程A首先运行并获取资源1, 然后进程B运行并获取资源2, 则无论接下来运行哪个, 它都不会继续前进, 但是两个进程都不会阻塞。实际发生的情况是, 它一遍又一遍地用完了CPU数量, 没有取得任何进展, 也没有任何阻塞。因此, 这种情况不是死锁(因为没有进程被阻塞), 但是在功能上等效于死锁:LIVELOCK。

是什么导致了活锁?

发生活锁可能以最令人惊讶的方式发生。在某些系统中, 允许的进程总数由进程表中的条目数决定。因此, 过程表插槽可以称为有限资源。如果由于表已满而导致派生失败, 则等待随机时间并重试将是程序执行派生的合理方法。

考虑具有100个处理插槽的UNIX系统。正在运行十个程序, 每个程序必须创建12个(子)进程。每个流程创建了9个流程之后, 10个原始流程和90个新流程耗尽了表格。现在, 这10个原始进程中的每个进程都处于无休止的循环中, 产生分叉和失败-这很容易出现死锁。发生这种情况的可能性很小, 但有可能发生。

死锁, 饥饿和活动锁之间的区别:

活动锁类似于死锁,不同之处在于活动锁中涉及的进程的状态相互之间不断变化,没有进展。Livelock是资源匮乏的一个特例;一般的定义只是说明一个特定的过程没有进展。

活锁:

var l1 = .... //lock object like semaphore or mutex etc
var l2 = .... //lock object like semaphore or mutex etc
      
         //Thread1
         Thread.Start( ()=> {
              
     while ( true ) {
          
         if (!l1.Lock(1000)) {
             continue ;
         }
          
         if (!l2.Lock(1000)) {
             continue ;
         }
          
         ///do some work
     });
  
         //Thread2
         Thread.Start( ()=> {
              
         while ( true ) {
              
             if (!l2.Lock(1000)) {
                 continue ;
             }
              
             if (!l1.Lock(1000)) {
                 continue ;
             }
              
             //do some work
         });

僵局:

var p = new object();
lock(p)
{
     lock(p)
     {
         //deadlock. Since p is previously locked
         //we will never reach here...
     }

一种僵局是一组动作的每个成员都在等待其他成员释放锁的状态。一种活锁另一方面, 几乎与死锁类似, 不同之处在于, 活锁中涉及的进程的状态彼此之间不断变化, 没有进展。因此, 如一般定义中所述, Livelock是资源匮乏的特例, 该过程没有进展。

饥饿:

饥饿是一个与活锁和死锁密切相关的问题。在动态系统中, 对资源的请求不断发生。因此, 需要一些策略来决定谁何时获得资源。这个过程是合理的, 可能会导致某些过程即使没有死锁也永远不会得到服务。

Queue q = .....
  
           while (q.Count & gt; 0)
{
     var c = q.Dequeue();
     .........
  
         //Some method in different thread accidentally
         //puts c back in queue twice within same time frame
         q.Enqueue(c);
     q.Enqueue(c);
  
     //leading to growth of queue twice then it
     //can consume, thus starving of computing
}

当”贪婪”线程使共享资源长期不可用时, 就会发生饥饿。例如, 假设一个对象提供了一个同步方法, 通常需要很长时间才能返回。如果一个线程频繁调用此方法, 则也需要频繁同步访问同一对象的其他线程将经常被阻塞。


赞(0)
未经允许不得转载:srcmini » 进程和线程:死锁,饥饿和活动锁

评论 抢沙发

评论前必须登录!