2011-01-17 9 views
1

軽量スレッド(ファイバー)用のラウンドロビンスケジューラを作成しようとしています。可能な限り多くの同時にスケジュールされたファイバを処理するには、スケーラビリティが必要です。私はまた、実行ループ以外のスレッドからファイバをスケジュールできるようにする必要があります。また、任意のスレッドからスケジューリングを解除することをお勧めします(ただし、実行ループからスケジューリングを解除できるだけです)。軽量スレッドセーフスケジューラの提案

私の現在のアイデアは、各ファイバーがノードであり、スケジューラーが現在のノードへの参照を保持する循環的な二重リンクリストを持つことです。あなたが見ることができるように

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のようなものを使用し、ロックされていないノードが見つかるまでリストを歩いていきます。

私が言及した要件を満たしている限り、全体として、私は特定の実装に投資していません。どんなアイデアでも大歓迎です。ありがとう!

EDIT -コメントは、私がwinapi繊維(私はそうではない)について話していると人々が思っていることを示しています。一言で言えば、私がしたいのは、スレッドでのスレッドを1つずつ実行するようにコードをスケジュールすることだけです。これはTPL/Async CTPに似ていますが、AFIKはUIスレッドでない限り、同じスレッドで継続を保証するものではありません。私は上記を実装する方法について別の提案をしていますが、単に「繊維を使用しない」と言ってはいけません。

+0

スレッドプールまたはBackgroundworkerで何が問題になっていますか? –

+0

私はOSレベルのスレッドよりも安価なものを撮影しています。私は何万(またはそれ以上)の繊維を予定しており、自分自身をスケジューリングしスケジューリングすることを望んでいます – chkn

+0

犯罪はありませんが、将来的に私は煩わしさ、痛み、失敗を予見します。私はwinapiにファイバーを書き込もうとしたことは一度もありませんでした。私はこれまでになかったプログラマーやプログラムの数が少なかったと思います。その理由は、ユースケースに合ったプログラムの量が0くらいだからです。すべてのプログラムの000001%。 (それ、私はおそらく技術的な知識が不足している)。 –

答えて

0

.NETでFibersを使用しないでください。この件についてはJoe Duffyのものをお読みください。ユーザモードスケジューラを正しく実装するには、代わりにTPLを使用します。

+0

私はwinapi繊維を使用していません。私は少し明確にするために私の質問を編集しました。 – chkn

2

タスク並列ライブラリを使用します。

MSDNに示すようにカスタムTaskSchedulerを作成します。カスタムタスクスケジューラでは、1つのスレッドだけが必要な場合は、スレッドを1つだけ持つことができます。

タスクをインラインでスケジュールできないようにするには、protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)を無効にしてfalseを返します。

0

私は、1つのスレッドでの実行は必須ではなく、一度に1つのタスクだけが完了するまで実行されると推測しています。この場合、TPL samplesをダウンロードしてください。 MaximumConcurrencyが1のLimitedConcurrencyLevelTask​​Schedulerを使用してください。

0

単純なlock-free message queueを使用してFuturesをキューに入れることができます。

循環データ構造を保持するのではなく、Resume()を呼び出す前にFutureをキューから引き出し、完了すると追加します(作業がさらに必要な場合)。

関連する問題