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,但我需要限制方法一次运行一个。所以 foreach 行中的一些DataGridView我想做类似的事情

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

但是,在上述方法中,该TaskSpin方法将立即退出,从而导致TaskSpin在另一个线程上分离下一个方法。这不好,因为该Run方法写入一组通用文件。将这些作业排队的最佳方式是什么?

4

4 回答 4

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 回答
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 回答
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 回答