1

すべて、TaskSpinTPL を使用してバックグラウンド スレッドで渡されたメソッドを実行するメソッドが呼び出されました。

public TaskSpin(Func asyncMethod, object[] methodParameters)
{
    ...
    asyncTask = Task.Factory.StartNew<bool>(() => 
        asyncMethod(uiScheduler, methodParameters));

    asyncTask.ContinueWith(task =>
    {
        ...
        // Finish the processing update UI etc.
        ...
        if (isStacked && task.Status == TaskStatus.RanToCompletion)
            ProcessNextTask(dataGridViewSite);
    }
    ...
}

このルーチンは、一度に 1 つのメソッドを起動するために十分に確立されていますが、最近、複数のメソッドをキューに入れ、それらを順番に実行する必要があります。これを行うには(この回答を参考に)、次のボタンクリックイベントハンドラーを作成しました

private void buttonProcAllUrg_Click(object sender, EventArgs e)
{
    // Build the stacker.
    Dictionary<Func<TaskScheduler, object[], bool>, object[]> taskPair = 
        new Dictionary<Func<TaskScheduler, object[], bool>, object[]>();
    this.taskQueue = 
        new Queue<KeyValuePair<Func<TaskScheduler, object[], bool>, object[]>>();

    // Populate the stacker.
    foreach (DataGridViewRow row in this.DataGridViewDrg.Rows) 
    {
        KeyValuePair<Func<TaskScheduler, object[], bool>, object[]> task =
            new KeyValuePair<Func<TaskScheduler, object[], bool>, object[]>
            (BuildDrgDataForSite, DrgDataRowInfo(row.Index));     
        this.taskQueue.Enqueue(task); 
    }

    // Launch the queue.
    ProcessNextTask(this.DataGridViewDrg);
}

そして、ProcessNextTask メソッドは次のように定義されます。

private void ProcessNextTask(DataGridView dataGridView) 
{     
    try     
    {
        // Queue empty.
        bIsStacked = true;
        if (this.taskQueue.Count <= 0)
        {
            bIsStacked = false;
            return;
        }

        // Launch new task.
        KeyValuePair<Func<TaskScheduler, object[], bool>, object[]> item = this.taskQueue.Dequeue();
        Task<bool> asyncBuildDataTask = null;
        TaskSpin(asyncBuildDataTask, uiScheduler, mainForm,
                    item.Key, item.Value, dataGridView,
                    "Process stack successfully executed", true, bIsStacked);
    }     
    catch(InvalidOperationException)    
    {
        // Nothing left in stack.
        bIsStacked = false;    
    }   
} 

これは問題なく動作しますが、最初のタスクが実行された後ProcessNextTask、継続から 2 回目 (またはそれ以上) が呼び出されると、GUI が応答しなくなります。2 回目の呼び出しで UI スレッドがブロックされないようにするにはどうすればよいですか?

ノート。ProcessNextTaskUI スレッド同期コンテキストを使用して別のスレッドでメソッドを起動しようとしました

Task task = Task.Factory.StartNew(() =>
{
    ProcessNextTask(dataGridView);
}, CancellationToken.None, 
   TaskCreationOptions.LongRunning, 
   uiScheduler);

どこでTaskScheduler uiSchduler = TaskScheduler.FromCurrentSynchonisationContex();

編集:BlockingCollection以下の@Reed Copseyの回答に基づいて、やりたいことを容易にするために作成しようとしましたが、これを行ったことはなく、ここでアドバイスを歓迎します

BlockingCollection<Action<object[]>> taskQueue = 
    new BlockingCollection<Action<object[]>>();
foreach (DataGridViewRow row in this.DataGridViewDrg.Rows) 
{
    Action<object[]> tmpAction = del => 
        {AsyncBuildDrgDataForSite(DrgDataRowInfo(row.Index)); };
    taskQueue.Add(tmpAction);
}

御時間ありがとうございます。

4

1 に答える 1

2

これについては、別の方法で検討することをお勧めします。

「ジョブをスタック」しようとしている場合は、通常、ルーチンをプロデューサー/コンシューマー シナリオに作り直す方が適切です。 BlockingCollection<T>は、作業項目を追加するために使用できるスレッド セーフ キューを提供し、それらを処理するタスクを ( 経由でGetConsumingEnumerable()) 作成するため、適切なフレームワークを提供します。項目が完了したら、UI を更新し、新しい項目を追加するときに「プロデューサー」タスクを再起動できます。

于 2012-08-01T15:33:00.497 に答える