Я пытаюсь написать циклический планировщик для легких потоков (волокон).Он должен масштабироваться для обработки как можно большего количества одновременно запланированных волокон.Мне также нужно иметь возможность планировать волокна из потоков, отличных от того, в котором запущен цикл выполнения, и желательно также отменять планирование их из произвольных потоков (хотя я мог бы жить только с возможностью отмены планирования из цикла выполнения).
Моя текущая идея состоит в том, чтобы иметь круговой двусвязный список, в котором каждое волокно является узлом, а планировщик содержит ссылку на текущий узел.Это то, что я имею до сих пор:
using Interlocked = System.Threading.Interlocked;
public class Thread {
internal Future current_fiber;
public void RunLoop () {
while (true) {
var fiber = current_fiber;
if (fiber == null) {
// block the thread until a fiber is scheduled
continue;
}
if (fiber.Fulfilled)
fiber.Unschedule ();
else
fiber.Resume ();
//if (current_fiber == fiber) current_fiber = fiber.next;
Interlocked.CompareExchange<Future> (ref current_fiber, fiber.next, fiber);
}
}
}
public abstract class Future {
public bool Fulfilled { get; protected set; }
internal Future previous, next;
// this must be thread-safe
// it inserts this node before thread.current_fiber
// (getting the exact position doesn't matter, as long as the
// chosen nodes haven't been unscheduled)
public void Schedule (Thread thread) {
next = this; // maintain circularity, even if this is the only node
previous = this;
try_again:
var current = Interlocked.CompareExchange<Future> (ref thread.current_fiber, this, null);
if (current == null)
return;
var target = current.previous;
while (target == null) {
// current was unscheduled; negotiate for new current_fiber
var potential = current.next;
var actual = Interlocked.CompareExchange<Future> (ref thread.current_fiber, potential, current);
current = (actual == current? potential : actual);
if (current == null)
goto try_again;
target = current.previous;
}
// I would lock "current" and "target" at this point.
// How can I do this w/o risk of deadlock?
next = current;
previous = target;
target.next = this;
current.previous = this;
}
// this would ideally be thread-safe
public void Unschedule () {
var prev = previous;
if (prev == null) {
// already unscheduled
return;
}
previous = null;
if (next == this) {
next = null;
return;
}
// Again, I would lock "prev" and "next" here
// How can I do this w/o risk of deadlock?
prev.next = next;
next.previous = prev;
}
public abstract void Resume ();
}
Как вы можете видеть, моя проблема в том, что я не могу обеспечить порядок блокировки, поэтому я не могу заблокировать более одного узла без риска тупиковой ситуации.Или я могу?Я не хочу иметь глобальную блокировку для объекта Thread, поскольку количество конфликтов блокировок будет чрезмерным.Кроме того, меня не особо волнует положение вставки, поэтому, если я блокирую каждый узел отдельно, тогда Schedule () может использовать что-то вроде Monitor.TryEnter и просто продолжать идти по списку, пока не найдет разблокированный узел.Я не вкладываюсь ни в какую конкретную реализацию, если она соответствует требованиям, которые я упомянул.Любые идеи очень приветствуются.Спасибо!
РЕДАКТИРОВАТЬ - Комментарии показывают, что люди думают, что я говорю о волокнах winapi (а я нет).Короче говоря, все, что я хочу сделать, - это запланировать выполнение битов кода один за другим в потоке single .Он похож на TTP / Async CTP, но AFIK не гарантирует продолжения в том же потоке, если это не поток UI.Я открыт для альтернативных предложений о том, как реализовать вышеизложенное, но, пожалуйста, не говорите «не используйте волокна».