生产者 - 消费者问题扭曲

时间:2011-04-30 15:12:32

标签: java concurrency clojure

  • 生产者是有限的,应该是消费者。
    • 问题是何时停止,如何运行。
  • 通过任何类型的BlockingQueue进行通信。
    • 不能依赖中毒队列(PriorityBlockingQueue)
    • 不能依赖于锁定队列(SynchronousQueue)
    • 不能完全依赖offer / poll(SynchronousQueue)
    • 可能存在更多异国情调的队列。
  

在另一个(可能是懒惰的)seq上创建一个排队的seq。排队    seq将在后台生成一个具体的seq,并且可以起床    消费者面前的物品。 n或q可以是整数n缓冲区    size或java.util.concurrent BlockingQueue的实例。注意    如果读者超前,那么从一个序列中读取就会阻止    生产者。

http://clojure.github.com/clojure/clojure.core-api.html#clojure.core/seque

我到目前为止的尝试+一些测试:https://gist.github.com/934781

Java或Clojure的解决方案表示赞赏。

2 个答案:

答案 0 :(得分:0)

class Reader {

    private final ExecutorService ex = Executors.newSingleThreadExecutor();
    private final List<Object> completed = new ArrayList<Object>();
    private final BlockingQueue<Object> doneQueue = new LinkedBlockingQueue<Object>();
    private int pending = 0;

    public synchronized Object take() {
        removeDone();
        queue();
        Object rVal;
        if(completed.isEmpty()) {
            try {
                rVal = doneQueue.take();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            pending--;
        } else {
            rVal = completed.remove(0);
        }
        queue();
        return rVal;
    }

    private void removeDone() {
        Object current = doneQueue.poll();
        while(current != null) {
            completed.add(current);
            pending--;
            current = doneQueue.poll();
        }
    }

    private void queue() {
        while(pending < 10) {
            pending++;
            ex.submit(new Runnable() {

                @Override
                public void run() {
                    doneQueue.add(compute());
                }

                private Object compute() {
                    //do actual computation here
                    return new Object();
                }
            });
        }
    }
}

答案 1 :(得分:0)

不是我害怕的答案,而是一些言论和更多问题。我的第一个答案是:使用clojure.core/seque。生产者需要以某种方式告知消费者,以便知道何时停止,并且我假设生产元素的数量事先不知道。为什么你不能使用EOS标记(如果你的意思是队列中毒)?

如果我正确理解了您的替代seque实现,那么当元素从您的函数外部移出时,它将会中断,因为channelq在这种情况下将失去一步:频道将包含比#(.take q)中的元素更多的q个元素,导致它被阻止。可能有一些方法可以确保channelq始终处于同步状态,但这可能需要实现您自己的Queue类,并且它增加了太多的复杂性,我怀疑它是值得的。

此外,您的实现不区分正常的EOS和由于线程中断导致的异常队列终止 - 取决于您使用它的原因,您可能想知道哪个是哪个。就个人而言,我不喜欢以这种方式使用异常 - 在异常情况下使用异常,而不是正常的流控制。