所有,我有一个名为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线程?
注意。我尝试使用UI线程同步上下文
在另一个线程上启动ProcessNextTask
方法
Task task = Task.Factory.StartNew(() =>
{
ProcessNextTask(dataGridView);
}, CancellationToken.None,
TaskCreationOptions.LongRunning,
uiScheduler);
其中TaskScheduler uiSchduler = TaskScheduler.FromCurrentSynchonisationContex();
。
编辑:我已经试图根据@Reed Copsey的答案创建一个BlockingCollection
来促进我想做的事情,但我从未这样做过,欢迎任何建议< / EM>
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);
}
感谢您的时间。
答案 0 :(得分:2)
我建议不同地考虑这个问题。
如果您正在尝试“堆叠工作”,通常最好将例程重新编写为生产者/消费者方案。 BlockingCollection<T>
为此提供了适当的框架,因为它提供了一个线程安全队列,您可以使用它来添加工作项,然后创建一个任务来处理它们(通过GetConsumingEnumerable()
)。项目完成后,您可以更新UI,然后在添加新项目时重新启动“生产者”任务等。