4

並行して実行されるさまざまなタスクを処理するプログラムがあります。単一のタスクが一種のマネージャーとして機能し、次のタスクが実行される前に特定の条件が満たされていることを確認します。ただし、タスクが非常に長い間WaitingToRun状態になることがあることがわかりました。次のコードは次のとおりです。

mIsDisposed = false;
mTasks      = new BlockingCollection<TaskWrapper>(new ConcurrentQueue<TaskWrapper>());

Task.Factory.StartNew(() => {
    while (!mIsDisposed) {
         var tTask = mTasks.Take();
         tTask.task.Start();
         while (tTask.task.Status == TaskStatus.WaitingToRun) {
             Console.WriteLine("Waiting to run... {0}", tTask.task.Id);
             Thread.Sleep(200);
         }
         tTask.ready.Wait();
     }
     mTasks.Dispose();
});

DoWork();
DoWork();
DoWork();
DoWork();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWorkAsync();
DoWork();

TaskWrapperは、非常に単純に次のように定義されます。

private class TaskWrapper
{
    public Task task  { get; set; }
    public Task ready { get; set; }
}

また、タスクは現在2か所でのみ追加されています。

public void DoWork()
{
    DoWorkAsync().Wait();
}

public Task DoWorkAsync()
{
    ManualResetEvent next = new ManualResetEvent(false);

    Task task  = new Task(() => ActualWork(next));
    Task ready = Task.Factory.StartNew(() => next.Wait());

    mTasks.Add(new TaskWrapper() {
        task  = task,
        ready = ready
    });
    return task;
}

どこにActualWork(next)電話しますかnext.Set()

このキューは作業を開始し、設定されるまで待機しnextてから、次の作業項目を続行できるようにします。タスク全体が終了するのを待ってから続行するDoWork()か、複数のタスクを一度にキューに入れることができます(設定後に実行されることになっnextています)。

ただし、を介してタスクを追加するとDoWorkAsync()、を呼び出した後tTask.task.Start()tTask.taskWaitingToRun状態で長時間(30秒から1分など)待機し、魔法のように実行を開始します。whileループを使用してこれを監視しましたがWaiting To Run... #、かなり長い間表示されます。

呼び出しDoWork()は常にすぐに実行されます。Waitこれは、実行するように設定されているタスクの呼び出しと関係があると確信しています。

ここで途方に暮れています。

アップデート:

私はなんとかコードを機能させることができましたが、そもそもなぜ問題があるのか​​知りたいのです。

いくつかの実験的な変更の後、私は自分の問題をなんとか修正することができましたが、それは良い修正というよりはむしろ「ああ、それで私はそれをすることができない」ということです。私の問題は、タスクの実行が速すぎることでした。DoWorkAsync()使用しないように変更し、にTask.Factory.StartNew変更することで、問題を解決することができました。tTask.ready.Wait()tTask.ready.RunSynchronously

TaskSchedulerタスクのスケジュールが遅れている理由はありますか?いくつかの基礎となるリソースを飽和させていますか?何が起きてる?

4

3 に答える 3

6

スレッドはシステムのスレッドプールで実行されます。スレッドプールには、常に使用可能な最小数のスレッドがあります(ThreadPool.SetMinThreads()を参照)。それ以上の数のスレッドを作成しようとすると、新しいスレッドが開始するたびに約500ミリ秒の遅延が発生します。

スレッドプールには最大数のスレッドもあり(ThreadPool.GetMaxThreads()を参照)、その制限に達すると、新しいスレッドは作成されません。古いスレッドが終了するまで待ってから、新しいスレッドをスケジュールします(つまり、もちろん、古いスレッドを再スケジュールして新しいスレッドを実行します)。

ただし、その制限に達する可能性は低いです。おそらく1000を超えています。

于 2013-02-01T08:52:12.227 に答える
0

ちょうど同様の問題に直面しました。

inifiteループを実行している同様のタスクがたくさんあり、そのタスクの1つは時々WaitingToRun状態のままになります。

そのようにタスクを作成することは私にとってトリックでした:

_task = new Task(() => DoSmth(_cancellationTokenSource.Token), TaskCreationOptions.LongRunning);
_task.Start();
于 2019-07-02T12:30:21.130 に答える
0

わかりました、私はちょうど同様の問題に直面しました。タスクを作成して開始したコードは実行されましたが、タスクは開始されませんでした(ステータスがWaitingToRunに変更されただけです)

このスレッドの他のオプションを試しても役に立たなかったので、もう少し考えてみました。このメソッドを呼び出しているコード自体が、UIタスクスケジューラで実行するように指定された継続タスクで呼び出されていることに気付きました(As UIを更新する必要がありました)...

だから何かのような

void Main()
{
    var t1 = new Task(() => Console.WriteLine("hello, I'm task t1"));
    t1.ContinueWith(t => CreateAndRunASubTask(), TaskScheduler.FromCurrentSynchronizationContext());
    t1.Start();

    Console.WriteLine("All tasks done with");
}

// Define other methods and classes here
public void CreateAndRunASubTask()
{
    var tsk = new Task(() => Console.WriteLine("hello, I'm the sub-task"));
    tsk.Start();
    Console.WriteLine("sub-task has been told to start");
    tsk.Wait();
    // the code blocks on tsk.Wait() indefinately, the tsk status being "WaitingToRun"
    Console.WriteLine("sub-task has finished");
}

修正は非常に簡単であることが判明しました。継続タスクを指定する場合は、TaskContinuationOption:TaskContinuationOptions.HideSchedulerを指定する必要があります。

これには次のような効果があります...(XMLコメントから取得)

System.Threading.Tasks.Task.Run(System.Action)やSystem.Threading.Tasks.Task.ContinueWith(System.Action {System.Threading.Tasks.Task})などのメソッドを呼び出して継続によって作成されたタスクを指定します
この継続が現在のスケジューラとして実行されているスケジューラではなく、デフォルトのスケジューラ(System.Threading.Tasks.TaskScheduler.Default)を参照してください。

すなわち(私の例では)

t1.ContinueWith(t => 
    CreateAndRunASubTask(), 
    System.Threading.CancellationToken.None, 
    TaskContinuationOptions.HideScheduler,
    TaskScheduler.FromCurrentSynchronizationContext());

それがしばらくの間私を困惑させたので、これが誰かを助けることを願っています!

于 2019-11-22T14:01:14.403 に答える