我正在尝试为轻量级线程(光纤)编写循环调度程序。它必须扩展以处理尽可能多的同时调度的光纤。我还需要能够从运行循环所在的线程之外的线程中调度光纤,并且最好还从任意线程中取消调度它们(尽管我只能从运行循环中取消调度它们)。
我目前的想法是建立一个循环的双向链表,其中每个光纤都是一个节点,调度程序保存对当前节点的引用。这就是我到目前为止所做的:
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纤维(我不是)。简而言之,我想要做的就是安排一些代码在单个线程上一个接一个地运行。它类似于TPL / Async CTP,但AFIK不保证在同一个线程上继续,除非它恰好是UI线程。我愿意接受有关如何实施上述内容的其他建议,但请不要只说“不要使用光纤”。
答案 0 :(得分:2)
使用任务并行库。
创建自定义TaskScheduler
,如MSDN所示。在自定义任务计划程序中,如果只需要一个线程,则只能有一个线程。
如果您想阻止它内联计划任务,请覆盖protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
并返回false
。
答案 1 :(得分:0)
不要将Fibers与.NET一起使用,请阅读Joe Duffy关于此主题的任何内容。使用TPL代替正确实现用户模式调度程序。
答案 2 :(得分:0)
我猜测不需要在单个线程上运行,一次只有一个任务运行完成。如果是这种情况,请下载TPL samples。查看使用MaximumConcurrencyLevelTaskScheduler,MaximumConcurrency为1。
答案 3 :(得分:0)
您应该能够使用简单的lock-free message queue对期货进行排队。
不是保留循环数据结构,而是在调用Resume()之前将Future拉出队列,并在完成时附加它(如果还有更多工作要做)。