【问题标题】:Producer/Consumer FIFO Semaphore生产者/消费者 FIFO 信号量
【发布时间】:2018-03-04 18:50:08
【问题描述】:

所以,我一直在研究生产者/消费者问题,我有 10 个生产者和 10 个消费者。消费者从这个 FIFO 中检索一个由生产者生成的数字。所有的消费者和生产者都应该同时工作(它们是线程)。现在,我现在不太擅长信号量,这就是我试图解决这个问题的原因。无论如何我都无法让它工作。

semPop 是 pop 操作的信号量。 semPush 是用于推送操作的信号量。 semWorkPush 是一种信号量,可防止多个线程在一个推送操作中同时工作。 semWorkPop 的作用与 semWorkPush 相同,但用于弹出操作。

另外,欢迎对我编写代码的方式提出建议,以使其更易于阅读! 谢谢

public class SharedFifo {
    private Integer[] memory;
    private Integer[] ids;
    private Semaphore semPop = new Semaphore();
    private Semaphore semPush = new Semaphore();
    private Semaphore semWorkPush = new Semaphore();
    private Semaphore semWorkPop = new Semaphore();
    private int numberOfElements = 0;
    private int totalSize = 0;
    private int nextMemberToPop = 0;
    private int tail = 0;
    private int flag = 0;

    public SharedFifo(int a) {
        semPop.up();
        semPush.up();
        semWorkPush.up();
        semWorkPop.up();
        this.totalSize = a;
        this.memory = new Integer[a];
        this.ids = new Integer[a];
    }

    public void pushVal(int val, int id) {
        if (numberOfElements == totalSize) {
            semPush.down();
            semPop.up();
        }
        semWorkPush.down();
        numberOfElements++;
        memory[tail] = val;
        ids[tail] = id;
        this.tail = (tail + 1) % (this.totalSize);
        if(flag > 0) {
            System.out.println("flag is bigger than 0");
            semPop.up();
            this.flag--;
        }
        semWorkPush.up();
    }

    public Integer[] popVal() {
        Integer[] valAndId = new Integer[2];
        if (numberOfElements == 0) {
            semPop.down();  
            semPush.up();
            this.flag++;
        }
        semWorkPop.down();
        valAndId[0] = memory[nextMemberToPop];
        valAndId[1] = ids[nextMemberToPop];
        this.nextMemberToPop = (this.nextMemberToPop + 1) % (this.totalSize);
        numberOfElements--;
        semPush.up();
        semWorkPop.up();
        return valAndId;
    }
}

【问题讨论】:

  • 在没有通读所有代码的情况下,最简单的方法就是使用ConcurrentLinkedQueue<E>,它还具有非阻塞的简洁属性(这意味着它可以很好地符合阿姆达尔定律.)
  • 我想你最好去codereview stackexchange 发表你的问题。
  • 另外,您似乎并未发布所有代码。 numberOfElements 定义在哪里?
  • @markspace 为了简洁起见,我没有发布所有代码,基本上,问题似乎仅与信号量有关,这就是原因。另外,尽管我相信使用 ConcurrentLinkedQueue 会更容易,但解决这个问题的目的确实是学习如何在这种环境中使用信号量,包括制作自己的 fifo 样式结构。
  • @markspace 我已经更新了代码,使其包含 numberOfElements 的定义。你是对的,我没有很好地解释错误。正在发生的事情是一些消费者正在检索 null 而不是生产者产生的数字。当你陷入一个问题时,很难记住解释的所有步骤。

标签: java fifo consumer producer


【解决方案1】:

你处理这件事的方式太复杂了,我觉得难以理解。很抱歉这么说,但这是真的。你有这么多的标志和计数缓冲区的方法,我对你有错误并不感到惊讶。你可能只是在犯数学错误。

您只需要两个数字。通常是一个计数和一个头指针。三个数字会让它更容易,头部,尾部和计数。如果在缓冲区已满时允许头部和尾部成为相同的索引,则需要计数。 (我在这里谈论的是循环缓冲区,我认为您正在尝试实现它。)

这里有一些用于访问循环缓冲区的基本数学方程式。我在这里故意使用原语,只是wait() 和notify(),真正的代码应该使用来自java.util.concurrent 的类之一。

class CircularBuffer {
   private final int[] buffer = new int[20];
   private int head;
   private int count;
   public synchronized void put( int i ) throws InterruptedException {
      while( count == buffer.length ) wait();// full
      buffer[head++] = i;
      head %= buffer.length;
      count++;
      notifyAll();
   }
   public synchronized int get() throws InterruptedException {
      while( count == 0 ) wait(); // empty
      int tail = (head - count) % buffer.length;
      tail = (tail < 0) ? tail + buffer.length : tail;
      int retval = buffer[tail];
      count--;
      notifyAll();
      return retval;
   }
}

看看这段代码有多小(和简单)?这就是你所需要的。

【讨论】:

  • 是的,这确实是一种更漂亮且更易于理解的解决方案。但是,我被特别要求在此解决方案中使用信号量。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-05-21
  • 1970-01-01
  • 1970-01-01
  • 2016-06-21
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多