3

すべて、私は と呼ばれる一般的なメソッドを持っていますTaskSpin。このメソッドでは、関連付けられた を起動Taskしますcontinutation

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

    asyncTask.ContinueWith(task =>
    {
        // Finish the processing update UI etc.
    }
    ...
}

問題は、 を使用して複数のメソッドを実行したいということですがTaskSpin、メソッドを一度に 1 つずつ実行するように制限する必要があります。だから、いくつかの行の foreachDataGridView私は次のようなことをしたい

foreach (DataGridViewRow row in this.DataGridViewUrg.Rows)
    TaskSpin(Run(DrgDataRowInfo(row.Index)));

ただし、上記では、TaskSpinメソッドはすぐに終了TaskSpinし、さらに別のスレッドで次のメソッドがスピンオフされます。Runメソッドは共通のファイル セットに書き込むため、これは適切ではありません。これらのジョブをキューに入れる最良の方法は何ですか?

4

4 に答える 4

2

継続チェーンを使用して、タスクの「キュー」を実装できます。これは読みやすく理解しやすく、期待どおりに機能します。また、「キューイング」ロジックが TaskSpin 内に含まれるようになりました。

private Task lastTask;

public void TaskSpin(Func asyncMethod, object[] methodParameters)
{
    ...
    if(lastTask == null)
        asyncTask = Task.Factory.StartNew<bool>(() => 
            asyncMethod(uiScheduler, methodParameters));
    else 
        asyncTask = lastTask.ContinueWith(t => 
            asyncMethod(uiScheduler, methodParameters));

    lastTask = asyncTask;

    asyncTask.ContinueWith(task =>
    {
        // Finish the processing update UI etc.
    }
    ...
}

これにより、最後のタスクが完了した後にのみ、新しい各タスクが実行されるようになります。

編集: UI タスクをシーケンシャル キューに含める場合は、簡単な変更を行います。

private Task lastTask;

public void TaskSpin(Func asyncMethod, object[] methodParameters)
{
    ...
    if(lastTask == null)
        asyncTask = Task.Factory.StartNew<bool>(() => 
            asyncMethod(uiScheduler, methodParameters));
    else 
        asyncTask = lastTask.ContinueWith(t => 
            asyncMethod(uiScheduler, methodParameters));

    lastTask = asyncTask.ContinueWith(task =>
    {
        // Finish the processing update UI etc.
    }
    ...
}
于 2012-08-01T11:37:53.660 に答える
2

独自のタスク キューを実装し、各タスクが完了した後、空になるまでキューを処理し続けることができます。

using TaskPair = KeyValuePair<Func, object[]>;
...

private Queue<TaskPair> taskQueue;
...

// generate the queue of tasks
this.taskQueue = new Queue<TaskPair>(this.DataGridViewUrg.Rows);
foreach (DataGridViewRow row in this.DataGridViewUrg.Rows)
{
    var task = new TaskPair(Run(DrgDataRowInfo(row.Index)), /* params */);
    this.taskQueue.Enqueue(task);
}
// initiate queue processing
ProcessNextTask();

....
private void ProcessNextTask()
{
    try
    {
        var item = this.taskQueue.Dequeue();
        TaskSpin(item.Key, item.Value);
    }
    catch(InvalidOperationException)
    {
        // queue is empty
    }   
}

....
// Execute task and process next in queue (if applicable)
public TaskSpin(Func asyncMethod, object[] methodParameters)           
{            
    ...           
    asyncTask = Task.Factory.StartNew<bool>(() =>            
        asyncMethod(uiScheduler, methodParameters));           

    asyncTask.ContinueWith(task =>           
    {           
        // Finish the processing update UI etc.
        ProcessNextTask();           
    }  
    ...                 
}
于 2012-08-01T11:29:40.567 に答える
0

続行する前に、Runが必要とするオブジェクトをロックして同期を実装することを検討する必要があります。あなたは次のようなものを望みます:

// locking object for write synchronization
object syncObj;

...

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

    asyncTask.ContinueWith(task =>
    {
        lock(syncObj)
        {
            // Finish the processing update UI etc.
        }
    }
    ...
}

public void Run()
{
    lock(syncObj)
    {
        // write results to common file
    }
}

ただし、これによってタスクが正常に完了するとは限りません。これはあなたにとって重要かもしれないように思われるので、おそらく私の提案はあなたが望むものではないかもしれません。

于 2012-08-01T11:32:25.443 に答える
0

UI の更新をシリアル化することが目標である場合、UI は GUI スレッドからのみ更新できるため、とにかくこれを行う必要があります。幸いなことに、これを行うメカニズムはすでに組み込まれています。

winformsを使用していることに注意してください。そのため、 SafeInvokeなどのメカニズムを使用することをお勧めします。これにより、GUI スレッドで実行するコードがキューに入れられます。

于 2012-08-01T11:48:13.203 に答える