1

所有,我有一个调用TaskSpin的方法,它使用 TPL 在后台线程上运行传递的方法。

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);
    }
    ...
}

这个例程已经很好地建立了一次触发一个方法,但我最近被要求将多个方法排队并按顺序运行它们。为此(由this answer辅助)我编写了以下按钮单击事件处理程序

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从延续开始第二次(或更多次)调用之后,GUI 变得无响应。我可以做些什么来确保 UI 线程在第二次调用时不会被阻塞?

笔记。我尝试ProcessNextTask使用 UI 线程同步上下文在另一个线程上启动该方法

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

哪里TaskScheduler uiSchduler = TaskScheduler.FromCurrentSynchonisationContex();

编辑:我已尝试根据@Reed Copsey 在下面的回答创建一个BlockingCollection以促进我想做的事情,但我从未这样做过,并欢迎在这里提供任何建议

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