【问题标题】:Manipulating shared data using mutex and semaphores使用互斥量和信号量操作共享数据
【发布时间】:2012-03-20 02:42:24
【问题描述】:

我希望有人能解决我在这个话题上的困惑。听起来很简单,但我真的很困惑。

在生产者/消费者问题中,我使用了 4 信号量解决方案。我为每个关键部分使用了不同的锁。 说,

Pseudo code of producer:
    wait(slot) // counting sem
       wait(mutex1) // binary sem
         rear <-- rear + 1
         buffer[rear] <-- item
       signal (mutex1)
    signal(items)

我使用“mutex2”作为消费者的第二个 Mutex,作为生产者中的“mutex1”。

现在,我的问题是。如果我的生产者和消费者没有使用缓冲区(后部和前部)而是使用堆栈,那么只有他们可以操作 [top]。为了确保互斥,我需要像在 4 信号量中那样使用一个互斥锁还是两个不同的锁。

 Pseudo code of consumer with stack:
         wait (message)
            wait (mutex)
              getspace <-- stack[top]
              top – 1
            signal (mutex)
          signal (slot)

就个人而言,我认为两个过程都需要一个锁,所以我确保生产者和消费者都不会同时访问顶部。但我不确定。 谢谢。

【问题讨论】:

    标签: mutex semaphore


    【解决方案1】:

    我不确定我是否 100% 遵循您的伪代码,但我会尽力解释如何使用信号量从生产者-消费者进程中管理堆栈。

    当您有一个跨多个线程访问的堆栈时,您需要在访问数据时锁定它,或者更具体地说,当它被推送和弹出时。 (这始终是生产者-消费者问题的基本假设。)

    我们首先定义一个用于锁定堆栈的互斥体。

    进程信号的全局声明

    stackAccessMutex = semaphore(1) # The "(1)" is the count 
                                    # initializer for the semaphore.
    

    接下来,当我们在消费者和生产者线程中添加或删除数据时,我们需要锁定它。

    生产者线程

    dataPushBuff #Buffer containing data to be pushed to the stack.
    
    …dataPushBuff is assigned…
    
    stackAccessMutex.wait()
        stack.push(dataPushBuff)
    stackAccessMutex.signal()
    

    消费者线程

    dataRecvBuff = nil # Defining a variable to store the pushed
                       # content, accessible from only within 
                       # the Consumer thread.
    
    stackAccessMutex.wait()
        dataRecvBuff = stack.pop()
    stackAccessMutex.signal()
    
    …Consume dataRecvBuff as needed since it's removed from the stack…
    

    到目前为止,一切都很简单。生产者仅在需要时才会锁定堆栈。对于消费者来说也是如此。我们不应该需要另一个信号量,不是吗?正确的?不,错!

    上面的场景做了一个致命的假设——堆栈总是在弹出之前用数据初始化。如果消费者线程在生产者线程有机会弹出任何数据之前执行,您将在消费者线程中生成错误,因为stack.pop() 不会返回任何内容!为了解决这个问题,我们需要通知消费者堆栈中的数据可用。

    首先,我们需要定义一个信号量,用于指示堆栈中的数据是否存在。

    进程信号的全局声明,版本 #2

    stackAccessMutex = semaphore(1)
    itemsInStack     = semaphore(0)
    

    我们将 itemsInStack 初始化为堆栈中的项目数,即 0 (参见 1)

    接下来,我们需要在生产者和消费者线程中实现新的信号量。首先,我们需要让 Producer 发出已添加项目的信号。现在让我们更新 Producer。

    生产者线程,版本 #2

    dataPushBuff
    
    …dataPushBuff is assigned…
    
    stackAccessMutex.wait()
        stack.push(dataPushBuff)
    stackAccessMutex.signal()
    itemInStack.signal() #Signal the Consumer, we have data in the stack!
                         #Note, this call can be placed within the 
                         #stackAccessMutex locking block, but it doesn't 
                         #have to be there.  As a matter of convention, any
                         #code that can be executed outside of a lock, 
                         #should be executed outside of the lock.
    

    现在我们可以通过信号量检查堆栈中是否有数据,让我们重新编写我们的消费者线程。

    消费者线程,版本 #2

    dataRecvBuff = nil # Defining a variable to store the pushed
                       # content, accessible from only within 
                       # the Consumer thread.
    
    itemsInStack.wait()
    stackAccessMutex.wait()
        dataRecvBuff = stack.pop()
    stackAccessMutex.signal()
    
    …Consume dataRecvBuff as needed since it's removed from the stack…
    

    ……就是这样。如您所见,有两个信号量,并且都是强制性的可用并在堆栈中没有任何内容时将其锁定。

    希望能回答您的问题。如果您有任何具体问题,我会更新我的回复。

    1. 理论上,当进程开始时,您可以 用数据预初始化你的堆栈。在这种情况下,您应该 can 使用以下值初始化您的 itemsInStack 信号量 等于堆栈数。然而,在这个例子中,我们 假设堆栈中没有数据,也没有 初始化。

    2. 值得一提的是,在一个特定的情况下,你 理论上可以只使用stackAccessMutex。 考虑堆栈始终包含数据的情况。如果 堆栈是无限的,我们不需要通知我们的消费者数据 已添加,因为总会有数据。然而,在 现实中不存在“无限堆栈”。即使那应该是 在您当前的上下文中的情况下,添加没有开销 itemsInStack 信号量的安全网。

      此外,放弃itemsInStack 计数可能很诱人 信号量如果在您当前的情况下调用 stack.pop() 如果不返回任何内容,则不会导致任何错误 空栈中的数据。

      这是合理的,但不推荐。假设消费者线程正在执行 循环上的代码,循环会不断执行栈消耗代码 while 没有要消耗的数据。通过使用itemsInStack 信号量,您正在暂停 线程直到数据到达,这应该可以节省一些 CPU 周期。

    【讨论】:

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