9

これは、10個のスレッドをバッチで開始し、5秒間待機して、バッチで停止するコンソールプログラムです。

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Action<int> act = (i) =>
        {
            Console.Write("start {0} ", i);
            Thread.Sleep(5000);
        };

        act.BeginInvoke(index, OnTaskComplete, index);
    });

    Console.ReadKey();
}

static void OnTaskComplete(IAsyncResult res)
{
    Console.Write("finish {0} ", res.AsyncState);
}

しかし、結果は私が期待したものではありません。10個のスレッドが1つずつゆっくりと開始し(約1秒間隔)、「開始」の前に「終了」が出てきます。

Thread.Sleepをコメントアウトすると、すべてのスレッドがフラッシュで開始および終了します。

Thread.Sleep他のスレッドに影響しますか?とにかく純粋なアイドル時間を作る方法はありますか?

/ - - - - - - - - - - - - - - -編集 - - - - - - - - - - ----------

同じ問題は次の場合にも発生します。

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Console.Write("start {0} ", index);
        Thread.Sleep(5000);
        Console.Write("fnish {0} ", index);
    });

    Console.ReadKey();
}

- - - - - - - - - - - 編集 - - - - - - - - - - - -

ついに私はthread.sleepを置き換える素敵な方法を見つけました

static void Main(string[] args)
{
    System.Threading.Tasks.Parallel.For(0, 10, (index) =>
    {
        Console.Write("start {0} ", index);

        var t1 = new System.Threading.Timer(new TimerCallback(MyTimerCallback), index, 5000, 0); 
    });

    Console.ReadKey();
}

static void MyTimerCallback(object o)
{
    Console.Write("Timer callbacked ");
}
4

3 に答える 3

13

これは仕様によるものです。スレッドプール マネージャーが、限られた数のスレッドを実行状態に維持しようとしていることがわかります。マシンの CPU コア数よりも多くのスレッドをプログラムが実行していないことを確認することが重要です。Windows がアクティブなスレッド間でコアのスワップを開始することを強制されると、これは非効率的です。スレッドプール マネージャーは、スレッドがスリープ状態であり、実際には何も実行していないことを認識できるほど賢くありません。

デュアルコア マシンでは、最初の 2 つのスレッドがすぐに開始されます。次に、アクティブなスレッドが進行しておらず、ブロックされている可能性があることにスレッド マネージャーが気付くと、1 秒間隔で 1 つずつ追加のスレッドの実行が許可されます。スレッドが解放されて Console.Write() 呼び出しが実行される順序は決定論的ではありません。

もちろん、これは人為的なテストであり、実際のスレッドはスリープしません。たとえば、I/O 要求が完了するのを待つなど、長時間ブロックするスレッドがある場合、スレッドプール スレッド (タスク) を使用することは最善の解決策ではありません。

于 2011-06-01T03:04:28.357 に答える
5

TaskCreationOptions.LongRunningThreadPool の制限を「削除」します。の簡単な
指定方法がわかりません。 ただし、Task クラスを使用して同じ効果を得ることができます。TaskCreationOptions.LongRunningParallel.For

Action<int> action = i =>
    {
        Console.Write("start {0} ", i);
        Thread.Sleep(5000);
        Console.Write("finish {0} ", i);
    };

var tasks = Enumerable.Range(0, 100)
    .Select(arg => Task.Factory.StartNew(() => action(arg), TaskCreationOptions.LongRunning))
    .ToArray();

Task.WaitAll(tasks);

それがなければTaskCreationOptions.LongRunning、あなたがしたのとまったく同じように動作しますParallel.For

于 2011-06-01T03:16:38.493 に答える
3

コンソールへの書き込み時に ThreadID を表示するようにコードを少し更新しました。

Console.WriteLine("start index:{0} thread id:{1} Time:{2} ", index, Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString());
Thread.Sleep(5000);
ConsoleWriteLine("finish index:{0} thread id:{1} Time:{2} ", index, Thread.CurrentThread.ManagedThreadId.ToString(), DateTime.Now.ToLongTimeString());

私のマシンはデュアルコアです。これが私が得た出力です。これにより、何が起こっているのかがわかります。ループは常に順番どおりに実行されるとは限らないことに注意してください。0 から 9 まで、並列で、配列のチャンクを取得し、各項目をラムダで実行します。

出力:

start  index:1 thread id:11 Time:11:07:17 PM
start  index:0 thread id:9  Time:11:07:17 PM
start  index:5 thread id:10 Time:11:07:17 PM
start  index:6 thread id:12 Time:11:07:18 PM
start  index:2 thread id:13 Time:11:07:19 PM
start  index:7 thread id:14 Time:11:07:20 PM
start  index:3 thread id:15 Time:11:07:21 PM
start  index:8 thread id:16 Time:11:07:22 PM
finish index:0 thread id:9  Time:11:07:22 PM
start  index:4 thread id:9  Time:11:07:22 PM
finish index:1 thread id:11 Time:11:07:22 PM
start  index:9 thread id:11 Time:11:07:22 PM
finish index:5 thread id:10 Time:11:07:22 PM
finish index:6 thread id:12 Time:11:07:23 PM
finish index:2 thread id:13 Time:11:07:24 PM
finish index:7 thread id:14 Time:11:07:25 PM
finish index:3 thread id:15 Time:11:07:26 PM
finish index:8 thread id:16 Time:11:07:27 PM
finish index:4 thread id:9  Time:11:07:27 PM
finish index:9 thread id:11 Time:11:07:27 PM
于 2011-06-01T03:12:01.273 に答える