本文介绍了使用任务延续来堆叠作业的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

All, I have a mathod called TaskSpin that runs the passed method on a background thread using 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 routine is well established to fire off one method at-a-time, but I have recently been required to queue multiple methods and run them sequentially. To do this (aided by this answer) I have written the following button click event handler

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 定义为

and the method ProcessNextTask is defined as

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 线程在第二次调用时不会被阻塞?

This works fine, but after the first task has run and the ProcessNextTask is called for the second (or more times) from the continuation, the GUI becomes unresponsive. What can I do to ensure the UI thread is not blocked upon the second call?

注意.我尝试使用 UI 线程同步上下文在另一个线程上启动 ProcessNextTask 方法

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

where TaskScheduler uiSchduler = TaskScheduler.FromCurrentSynchonisationContex();.

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

I have attampted to create a BlockingCollection to facilitate what I want to do based on @Reed Copsey's answer below, but I have never done this and would welcome any advice here

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

感谢您的时间.

推荐答案

我建议换个方式思考这个问题.

I would recommend thinking about this differently.

如果您正在尝试堆叠作业",通常最好将例程改造成生产者/消费者场景.BlockingCollection 为此提供了适当的框架,因为它提供了一个线程安全队列,您可以使用它来添加工作项,然后创建一个任务来处理它们(通过 GetConsumingEnumerable()).项目完成后,您可以更新您的用户界面,然后在添加新项目等时重新启动生产者"任务.

If you're trying to "stack jobs", it's typically better to rework the routine into a producer/consumer scenario. BlockingCollection<T> provides the proper framework for this, as it provides a thread safe queue which you can just use to add in your work items, and then create a task to process them (via GetConsumingEnumerable()). Once the items are done, you can update your UI, then restart the "producer" task as you add a new item, etc.

这篇关于使用任务延续来堆叠作业的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 00:02