У меня есть существующая система с несколькими длительными работами. Я надеялся найти простой способ обернуть эти операции в задачи и дать пользователю сообщение о состоянии занятости.Как обновить пользовательский интерфейс winforms от отдельных задач?
Это используется .net 3.5, и я использую задний порт TPL. К сожалению, async/wait не может быть использован здесь.
Я попытался придумать решение в простой тестовой форме, показанной ниже, и столкнулся с некоторыми проблемами, обворачивающими мою голову вокруг этого.
В приведенном ниже методе отображаются только первые 2-3 задания, после чего пользовательский интерфейс не обновляется до тех пор, пока все не будет завершено. Почему это?
private void RunTaskUpdateBefore (string msg)
{
UpdateUI (msg);
Task task = Task.Factory.StartNew (() =>
{
// simulate some work being done
Thread.Sleep (1000);
});
task.Wait();
}
Я надеялся, что этот метод будет работать, но не обновляет интерфейс, пока все задачи не будут выполнены
private void ChildTaskExternalWait (string msg)
{
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task task = Task.Factory.StartNew (() =>
{
Task.Factory.StartNew (() =>
{
UpdateUI (msg);
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
// simulate some work being done
Thread.Sleep (1000);
});
task.Wait(); // I assume this is the problem
}
Ниже метод отлично работает, но я не уверен, если я могу использовать его в мой проект. Это довольно сложная система, операции происходят во многих разных классах и контролируются унаследованной системой, которую мы не можем изменить. Я бы предположил, что мне нужно попытаться создать какую-то глобальную очередь задач для этого?
private Task RunTaskInternalWaitOnPrevious (string msg, Task t1 = null)
{
var uiScheduler = TaskScheduler.FromCurrentSynchronizationContext();
Task task = Task.Factory.StartNew (() =>
{
if (t1 != null)
{
t1.Wait();
}
Task.Factory.StartNew (() =>
{
UpdateUI (msg);
}, System.Threading.CancellationToken.None, TaskCreationOptions.None, uiScheduler);
// simulate some work being done
Thread.Sleep (2000);
});
return task;
}
Моя реализация тестовой формы:
private void button1_Click (object sender, EventArgs e)
{
// Only displays the first 2-3 tasks. After that UI doesn't get updated until all are complete
RunTaskUpdateBefore ("Task 1");
RunTaskUpdateBefore ("Task 2");
RunTaskUpdateBefore ("Task 3");
RunTaskUpdateBefore ("Task 4");
RunTaskUpdateBefore ("Task 5");
RunTaskUpdateBefore ("Task 6");
RunTaskUpdateBefore ("Task 7");
// this works with any number of tasks
// Unfortunately I'm not sure if I can use it in my application (see above notes)
//var t1 = RunTaskInternalWaitOnPrevious ("Task 1");
//var t2 = RunTaskInternalWaitOnPrevious ("Task 2", t1);
// and so on
}
private void UpdateUI (string msg)
{
this.textBox1.Text += msg;
this.textBox1.Text += System.Environment.NewLine;
}
обновление/уточнение
* Мои задачи не могут выполняться параллельно.
* Я хочу сообщить пользователю, что работа выполняется на каждом шаге.
* Во время выполнения задач другой пользовательский интерфейс не может работать.
Задачи не централизованы для конкретной части кода и происходят повсеместно. Я не знаю, как еще описать мою ситуацию, извините.
Я думаю, что в этот момент мне нужно начать с более короткого примера. – kbeal2k