【问题标题】:Mutual exclusion and semaphores互斥和信号量
【发布时间】:2011-04-20 12:18:32
【问题描述】:

我正在编写一个模拟男女通用浴室的程序(用于家庭作业)。一次只允许 4 人进入,如果异性已经在使用浴室,则男女不能进入。我的问题是最多允许 4 人进入浴室。从输出中可以看出,一次只有 1 人进入洗手间。这是我的代码:

const int Delayx = 60;
int i;
int restroom = 0;
int Menwaiting = 0;
int Womenwaiting = 0;
semaphore max_capacity;
semaphore woman;
semaphore man;
semaphore mutex;
semaphore restroomcount;
void Delay(void)
{
    int DelayTime;
    DelayTime = random(Delayx);
    for (i = 0; i<DelayTime; i++);
}

void Woman(void)
{
//  for(;;){
    Womenwaiting++;
    //wait(mutex);
    wait(woman);
    wait(max_capacity);
        //wait(woman);
        wait(mutex);
        wait(restroomcount);
        cout << "A Woman has entered Restroom"<<endl;
        cout << "People in the Restroom:" << restroom++ <<endl <<endl;
        signal(restroomcount);
        Womenwaiting--;
        Delay();
        wait(restroomcount);
        cout << "A woman has exited Restroom"<<endl;
        cout << "People in the Restroom:" << restroom-- <<endl<<endl;
        signal(restroomcount);
        signal(mutex);
        signal(max_capacity);
        if(Menwaiting > Womenwaiting){
              signal(man);
                  }
              else{
            signal(woman);
        }
        //signal(max_capacity);
    //signal(man);
//  }
}
void Man(void)
{
//  for(;;){
    Menwaiting++;
    //wait(mutex);
    wait(man);
    wait(max_capacity);
    //wait(man);
        wait(mutex);
        wait(restroomcount);
        cout <<"A Man has entered the Restroom"<<endl;
        cout <<"People in the Restroom:" << restroom++ <<endl<<endl;
        signal(restroomcount);
        Menwaiting--;
        //signal(mutex);
        Delay();
        //wait(mutex);
        wait(restroomcount);
        cout << "A man has exited the Restroom"<<endl;
        cout <<"People in the Restroom:" << restroom-- <<endl<<endl;
        signal(restroomcount);
        signal(mutex);
        signal(max_capacity);
        if(Womenwaiting > Menwaiting){
            signal(woman);
            }
        else{
            signal(man);
            }
        //signal(max_capacity);
        //signal(woman);
//}
}
void main()
{
    initialsem(woman,1);
    initialsem(man,1);
    initialsem(max_capacity,4);
    initialsem(mutex,1);
    initialsem(restroomcount,1);
    cobegin
    {
        Woman(); Woman(); Woman(); Woman(); Woman(); Man();  Man(); Man(); Man(); Man();
    }

}

这会生成以下输出:

一个男人进了洗手间
厕所里的人:1

一个男人离开了洗手间
厕所里的人:0

一个男人进了洗手间
厕所里的人:1

一个男人离开了洗手间
厕所里的人:0

一个女人进了洗手间
厕所里的人:1

一个女人已经离开了洗手间
厕所里的人:0

一个女人进了洗手间
厕所里的人:1

一个女人已经离开了洗手间
厕所里的人:0

等等,直到永远。

【问题讨论】:

  • 如果洗手间里已经有女性,该代码的哪一部分应该负责防止男性进入洗手间,反之亦然?
  • 这个分配应该是单线程的吗?使用线程?协程?
  • @dgnorton - 信号量。我正在使用一个名为 BACI 的解释器来模拟并发进程。
  • 我知道这是一个作业,但它真的需要使用信号量来完成吗(它是一个信号量练习)吗?条件变量可以更容易地描述男性/女性可以进入的条件:您只需在条件发生变化时发出信号,线程会检查自己是否可以进入。

标签: c++ mutex semaphore


【解决方案1】:

我认为你的信号量太多了。您的男人/女人信号量一次只针对 1 个人。考虑使用一些受互斥锁保护的状态变量(浴室的当前性别、浴室中的人数),而不是使用这么多不同的信号量。

您是否保持排队顺序,或者人们可以根据当前的厕所性别跳过吗?例如,如果你有女人,女人,女人,男人,女人,第4个女人是否允许跳过男人进入洗手间,或者让3个女人退出,然后男人进入/退出,然后女人可以进入?这是一个比允许跳过更容易的问题。

【讨论】:

    【解决方案2】:

    是否需要使用信号量?例如,在“c++”伪代码中,实现如下所示:

    首先让我们创建一个状态对象和一个验证状态之间转换的函数

    struct BathRoomState
    {
       int women;
       int men;
    
       BathRoomState( int w , int m ) : women(w) , men(m) {}
    
       bool hasWomen()
       { 
          if (women > 0 && men == 0)
             return true;
          return false;
       }
    
       bool isEmpty()
       {
          return (women + men == 0);
       }
    
       static bool isValidTransition( BathRoomState* a , BathRoomState* b )
       {
          if (a->HasWomen())
          {
            if ( (abs( a->women - b->women ) == 1) && (a->men == b->men) )
               return true;
            else false;
          } else if (a->isEmpty())
          {
              if ((b->women == 1 && b->men == 0)
                   || (b->women == 0 && b->men == 1))
                 return true else false;
          } else //a has men
          {
              if ((abs( a->men - b->men ) == 1) && ( a->women == b->women))
                return true else false;
          }
       }
    }
    

    让我们还创建一个对当前状态的全局引用和一个根据下一个所需状态更新当前状态的函数

    BathRoomState* currentBathroomState = 0;
    bool TryToChangeState(BathRoomState* newState)
    { 
      BathRoomState* existingState = currentBathroomState;
      if (BathRoomState::isValidTransition( existingState , newState ))
      {
         //this atomic operation depends on library support
         bool success = CompareAndSwapAtomically( currentBathroomState , existingState , newState );
         return success;
      }
    }
    

    然后我们创建一个全局向量来保存状态,以及一个代表试图去洗手间的女性线程的函数

    std::vector< BathRoomState* > noGCinThisExample;
    //thread functtion
    void women()
    {
       BathRoomState* existingState = currentBathroomState;
       BathRoomState* newState = new BathRoomState( existingState.women+1 , existingState.men );
       while (!TryToChangeState(newState))
       {
         //yield or sleep from time to time here to let other threads progress
         existingState = currentBathroomState;
         newState.women = existingState.women + 1;
         newState.men = existingState.men;
       }
       noGCinThisExample.push_back( newState ); //no GC in this example
       //the woman is in the bathroom now. lets give her some time
       delayForWomen();
       //lets try to get her out
    
       BathRoomState* exitState = new BathRoomState( existingState.women-1 , existingState.men );
       while (!TryToChangeState(exitState ))
       {
         //yield or sleep from time to time here to let other threads progress
         existingState = currentBathroomState;
         exitState.women = existingState.women - 1;
         exitState.men = existingState.men;
       } 
       noGCinThisExample.push_back( exitState); //no GC in this example
    }
    
    //homework: do a similar function for men
    

    以及带有流程循环逻辑和初始化的主函数

    void main()
    {
      BathRoomState* initialState = new BathRoomState( 0 , 0);
      noGCinThisExample.push_back( initialState );
      currentBathroomState = initialState;
      while(some_condition)
      {
       if (random() > 0.5)
         thread( women() );
       else
         thread( men() );
      }
    };
    

    这段代码应该可以工作(我还没有测试过)。我有点作弊,因为我没有删除任何创建的临时状态,所以每个状态都会持续到进程结束。正确的垃圾回收需要一种称为危险指针管理的技术。

    请注意,我不使用任何互斥信号量或锁,我使用的唯一锁定原语是 CAS(地址、旧值、新值)(比较和交换)。这个原语原子地比较一个指针(地址),如果它仍然包含(旧值),那么它分配它新值并成功,否则它失败。此外,您仍然需要一个全局锁来存储我未包含在代码中的状态的 std::vector在这些情况下如何使 GC 工作)

    由于我所有的中间状态都是不可变的(lisp/clojure 风格的不可变性),线程的争用(因此,饥饿)大大改善。在您的示例中,状态集很小(只是一群人),我们不删除使用过的状态还不错。

    然而,即使有我提到的问题,我想你也会同意正在发生的事情的逻辑更加明确和可读。

    【讨论】:

    • 你的 isEmpty() 方法是错误的。如果有人在洗手间里,当前的实现返回 true。我认为你想要 men+women
    • 你说得对,我修好了。谢谢。我不担心
    【解决方案3】:

    问题的问题
    原始代码不是很OO。

    浴室队列的处理应该与队列中的人的生成分开 - 如果至少在队列被填满后不运行单独的线程。

    假设基本上有男女分开的队列 - 没有以某种固定的顺序混合,否则使用信号量没有任何意义。

    问题没有描述条件合适的情况下有多少人可以进入,男厕所男多,你是填到4个还是等到男排再次少于女排?

    即使如此描述的问题(并且基于没有线程的示例代码)在我看来不适用于信号量,主要问题是信号量不能轻易产生计数并且成功的等待改变计数。

    我在这个问题中看到的有趣之处在于,在几乎相等的队列长度和禁止另一个同性进入厕所和在厕所里的其他人离开厕所之前的机会之间进行交易的效率低下。再次变大。让我们面对现实吧,它是男女皆宜的,所以它应该允许 4 人不分性别;)

    建议的解决方案
    所以你需要使用信号量,信号量的有趣之处在于记录多次使用(与互斥锁不同),如果没有可用空间,它可能会等待。但是它不会区分等待的那些,它只会告诉有可用空间。

    有 1 个信号量,并认为您应该在有人进入队列或有人离开浴室时检查信号量。

    然后,您可以为男性和女性分别设置 1 个“队列”(鉴于这基本上是一个计数)。这些队列在条目方面并不真正相关或相互限制,因此与信号量无关。每个都可以遵循无锁提供者模式,但您可能会发现使用互斥锁进行同步更容易,这样您就可以检查队列的大小并对其进行操作。在下文中,我只是直接使用了计数,而是应该使用某种形式的 InterlockedIncrement 和 InterlockedDecrement 来防止在同一个队列中添加和删除人员。

    粗略,浴室.h

    class Bathroom
    {
    public:
        Bathroom(void);
        ~Bathroom(void);
    
        AddMan();
        AddWoman();
        Run();
    private:
        StateChange();
    
        int m_Menwaiting;
        int m_Womenwaiting;
        semaphore max_capacity;
    
        enum Users {
            NOBODY ,
            WOMEN,
            MEN
        } m_inUseBy;
    };
    

    浴室.cpp

    Bathroom::Bathroom(void)
        : m_Menwaiting(0)
        , m_Womenwaiting(0)
        , m_inUseBy(NOBODY)
    {
        initialsem(max_capacity,4);
    }
    
    
    Bathroom::~Bathroom(void)
    {
        freesem(max_capacity);
    }
    
    Bathroom::AddMan(){
        ++m_Menwaiting;
        StateChange();
    }
    
    Bathroom::AddWoman(){
        ++m_Womenwaiting;
        StateChange();
    }
    
    Bathroom::StateChange() {
    
        // extra at a time
        if( m_Menwaiting > m_Womenwaiting && inUseBy != WOMEN ) {
            if( wait(max_capacity,0 delay) != timeout )
                m_Menwaiting--;
        }
    
        if( m_Womenwaiting > m_Menwaiting && inUseBy != MEN ) {
            if( wait(max_capacity,0 delay) != timeout )
                m_Womenwaiting--;
        }
    
        // all available slots
        if( m_Menwaiting > m_Womenwaiting && inUseBy != WOMEN ) {
            while( wait(max_capacity,0 delay) != timeout )
                m_Menwaiting--;
        }
    
        if( m_Womenwaiting > m_Menwaiting && inUseBy != MEN ) {
            while( wait(max_capacity,0 delay) != timeout )
                m_Womenwaiting--;
        }
    
    }
    
    Bathroom::run(){
    // people leaving bathroom simulated
        while(1) {
            Delay();
            signal(max_capacity);
            StateChange();
        }
    }
    

    程序.cpp

    Bathroom b1;
    
    addPeople() {
      while(true) {
      // randomly add people
        Delay();
        b1.AddMen();
        b1.AddWomen();
      }
    }
    
    int main(){
    
      thread( addPeople );
    
      b1.run();
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-08-24
      • 2016-08-30
      • 2012-09-01
      • 2015-10-25
      • 1970-01-01
      • 2011-01-05
      • 2011-04-10
      相关资源
      最近更新 更多