在我即将进行的并发系统考试的准备工作中,我试图从教科书“多处理器编程的艺术”中完成一些问题。一个问题是困扰我:
练习129:在我们的LockFreeStack对象中使用相同的共享BackOff对象进行推送和弹出是否有意义?我们怎样才能在EliminationBackOffStack中构建空间和时间的退避?
这个问题让我感到困惑,因为我想到的第一件事是它没有意义,因为所有退避对象都会让进程等待,所以为什么不分享呢?问题的第二部分完全没有我,任何帮助都是最受欢迎的。
LockFreeStack的代码:
public class LockFreeStack<T> {
AtomicReference<Node> top = new AtomicReference<Node>(null);
static final int MIN_DELAY = ...;
static final int MAX_DELAY = ...;
Backoff backoff = new Backoff(MIN_DELAY, MAX_DELAY);
protected boolean tryPush(Node node) {
Node oldTop = top.get();
node.next = oldTop;
return(top.compareAndSet(oldTop, node));
}
public void push(T value) {
Node node = new Node(value);
while (true) {
if (tryPush(node)) {
return;
} else {
backoff.backoff();
}
}
}
答案 0 :(得分:1)
我不确定是否有任何帮助,但无论如何我都会按“发布”按钮。
答案取决于backoff()
的实施。由于此处的目标是避免同步,因此不会有任何本地存储 - 可能是ThreadLocal
中的某些存储。如果退避算法使用随机数发生器,它也需要是可重入的。所以很可能你能够在pop
和push
之间分享它 - 现在你想要。由于push和pop都试图改变top
引用,如果后退给连续的线程提供了截然不同的数字,那将会很好。推或弹出更多的争论?我们是否需要采用一种或另一种方法更积极地退缩?如果这是一个通用堆栈,那么你就不会知道了。
关于退避如何“重组”,我也不确定。您是否可以使用成功的推送或弹出机会来减少退避时间? ThreadLocal
指定的序列中随机退避等待与素数之间的差异如何?
答案 1 :(得分:1)
从同步的角度来看待第一个问题,我认为为推送和弹出提供相同的BackOff
对象是有意义的。无论这个类的实现如何。这样做的原因是,如果我们有一个堆栈,我们必须在删除和添加项目到堆栈时保持一致的状态。但是,如果我们只是查看(查看堆栈的第一个元素或顶部)而不是我们可能有多个BackOff
对象查看它,因为读取不应该锁定有问题的数据源。第二个问题是要求为该类发布代码。
答案 2 :(得分:0)
在这种情况下,使用退避是过度杀戮。
任何真实世界的应用程序都应该花费更多时间来处理节点,而不是在堆栈上下移动。通过比较的堆栈操作应该非常短。因此,两个线程不太可能同时访问堆栈。但是,它有时会发生,因此您需要编写正确的代码。
public void push(T value) {
Node node = new Node(value);
while (!tryPush(node))
backoff.backoff();
}
恕我直言:可以缩短为
public void push(T value) {
Node node = new Node(value);
while (!tryPush(node));
}