9

MSDN ドキュメント TaskFactory.StartNew に従って、タスクを作成して開始します。したがって、以下のコードサンプルでは

class Program
{
    public static void Main()
    {
        var t =Task.Factory.StartNew(
                () => SomeLongRunningCalculation(10, Display)
            );
        var t1 = Task.Factory.StartNew(
                () => SomeLongRunningCalculation(20, Display)
            );
        Console.WriteLine("Invoked tasks");
        Task.WaitAll(t, t1);
        Console.ReadLine();
    }

    public static void Display(int value)
    {
        Console.WriteLine(value);
    }

    public static void SomeLongRunningCalculation(int j, Action<int> callBack)
    {
        Console.WriteLine("Invoking calculation for {0}", j);
        System.Threading.Thread.Sleep(1000);
        if (callBack != null)
        {
            callBack(j + 1);
        }
    }
}      

私の期待される出力は

Invoking calculation for 10
Invoking calculation for 20
Invoked tasks
11 
21

しかし、それは表示されています

Invoked tasks
Invoking calculation for 20
Invoking calculation for 10
21
11

私は学びたいです

  1. StartNew の直後にタスクが実行されないのはなぜですか?
  2. 期待される形式で出力を取得するにはどうすればよいですか?
4

3 に答える 3

12

これは、シングル コア CPU を搭載したマシンで発生する可能性が非常に高い結果です。または、マルチコアCPUを搭載したもので可能で、他のことをするのにも忙しい.

タスクまたはスレッドの作成は、コードの実行を可能にする論理オペレーティング システム構造をセットアップするだけです。コアがビジー状態の場合、オペレーティング システムのスケジューラはすぐに実行を開始しませ。スレッドは、マシン上で実行されている他のすべてのスレッドと競合する必要があります。典型的な Windows セッションには、1000 程度のセッションがあります。1 秒間に 64 回、カーネルは割り込みを生成し、スケジューラは何が起こっているかを再評価して、別のスレッドが順番を取得する必要があるかどうかを確認します。ブロックされていないスレッド (ファイルやネットワーク パケットの読み取りなど、他のスレッドがジョブを完了するのを待機しているスレッド) は実行する資格があり、スケジューラは優先度が最も高いスレッドを選択します。すべてのスレッドが確実にチャンスを得られるように、スケジューラ内のいくつかの追加コードは、優先順位の値を調整します。

ここでのキーワードはチャンスです。スレッドのスケジューリングは非決定論的です。

Thread クラス、Task クラス、または ThreadPool クラスについては何も述べていないことに注意してください。オペレーティング システムがスレッドをスケジュールする方法について、ほとんど何もする権限がありません。可能なことは、スレッドが実行されないようにすること、つまりスレッド プール スケジューラの仕事だけです。

優先度は重要です。Thread.Priority または Task.Priority プロパティをいじって、結果に影響を与えることができます。オペレーティング システムのスケジューラは、そのプロパティで設定した基本優先度から常にスレッドの優先度を調整します。たとえば、優先度の高い別のスレッドを作成することによって、スレッドが実行されるを防ぐことはできません。

スレッドが予測可能な順序で実行されることを期待すると、最悪の種類のバグであるスレッド競合バグが発生します。2 番目に悪いのはデッドロックです。これらは、タイミングと利用可能なマシン リソースと負荷に依存するため、デバッグが非常に困難です。明示的に処理するコードを記述することによってのみ、特定の注文を確実に受け取ることができます。これは、Mutex やlockキーワードなどのスレッド プリミティブを使用して行います。また、そのようなコードをスニペットに追加しようとすると、並行性が失われたプログラムになってしまうことも注目に値します。つまり、Task を使用できなくなるということです。スレッドまたはタスクは、予測できない時間に実行する余裕ある場合にのみ役立ちます。

于 2012-04-22T09:35:24.880 に答える
7

StartNew の直後にタスクが実行されないのはなぜですか?

MSDN については、実行するタスクをStartNew()スケジュールします。

StartNew を呼び出すことは、そのコンストラクターの 1 つを使用して Task を作成し、次に Start を呼び出して実行をスケジュールすることと機能的に同等です。ただし、作成とスケジューリングを分離する必要がない限り、単純さとパフォーマンスの両方から StartNew が推奨される方法です。

TPL は ThreadPool スレッドを使用するため、特定のタスク実行のために ThreadPool スレッドを予約して開始するために、いくつかの作業を行う必要がある場合があります。TPL の TaskScheduler のような中間メカニズムなしで別のスレッドを明示的に実行する必要がある場合は、手動でスレッドを作成して開始し、明らかに継続のようなきちんとしたものはありません。

于 2012-04-22T08:02:55.377 に答える