EDIT: Как было замечено, я написал эту функцию с совершенно противоположной семантикой - enqueuing только в пустую очередь. Я исправил это имя, чтобы отразить это, и решил оставить его, как на всякий случай, кому-то будет интересно. Таким образом, это не правильный ответ на этот вопрос, но не downvote пожалуйста, если вы не найдете другую причину :)
Ниже является попыткой добавить EnqueueIfEmpty()
к реализации очереди в указанной статье. Я не проверял, что он работает или даже компилируется. Основная идея заключается в том, что вы вставляете новый узел сразу после головки (а не в хвост), при условии, что следующая следующая голова является нулевой (что является необходимым условием для пустой очереди). Я оставил дополнительные проверки, чтобы голова была равна хвосту, который, возможно, можно удалить.
public bool EnqueueIfEmpty(T item) {
// Return immediately if the queue is not empty.
// Possibly the first condition is redundant.
if (head!=tail || head.Next!=null)
return false;
SingleLinkNode<T> oldHead = null;
// create and initialize the new node
SingleLinkNode<T> node = new SingleLinkNode<T>();
node.Item = item;
// loop until we have managed to update the tail's Next link
// to point to our new node
bool Succeeded = false;
while (head==tail && !Succeeded) {
// save the current value of the head
oldHead = head;
// providing that the tail still equals to head...
if (tail == oldHead) {
// ...and its Next field is null...
if (oldhead.Next == null) {
// ...try inserting new node right after the head.
// Do not insert at the tail, because that might succeed
// with a non-empty queue as well.
Succeeded = SyncMethods.CAS<SingleLinkNode<T>>(ref head.Next, null, node);
}
// if the head's Next field was non-null, another thread is
// in the middle of enqueuing a new node, so the queue becomes non-empty
else {
return false;
}
}
}
if (Succeeded) {
// try and update the tail field to point to our node; don't
// worry if we can't, another thread will update it for us on
// the next call to Enqueue()
SyncMethods.CAS<SingleLinkNode<T>>(ref tail, oldHead, node);
}
return Succeeded;
}
Этот код имеет тонкую ошибку. Как говорится в документе (http://www.boyet.com/articles/ABAProblem.html), он работает на C# из-за сбора мусора. Это будет ** НЕ РАБОТАЕТ ** в C. –
Я использую систему подсчета ссылок, которая разрешила проблему ABA на узлах моей очереди. поэтому гарантируется, что узлы не будут освобождены до раннего. – luke
Я никогда не сталкивался с решением, основанным на подсчете количества ссылок, которое допускает бесплатное() включение элементов очереди. Я мог бы представить решение, основанное на подсчете количества ссылок, которое позволяло возвращать элементы в freelist, но это все. Вы говорите, что вы разработали алгоритм восстановления надежной памяти на основе ref count? –