我有一个现有系统,有几个长时间运行的操作。我希望找到一种简单的方法将这些操作包装在任务中,并为用户提供繁忙的状态通知。
这是使用.net 3.5而我正在使用TPL后端口。不幸的是,async / await不能在这里使用。
我试图用下面显示的简单测试形式提出解决方案,并遇到了一些问题。
以下方法仅显示前2-3个任务,之后UI不会更新,直到所有任务完成。这是为什么?
private void RunTaskUpdateBefore (string msg)
{
UpdateUI (msg);
Task task = Task.Factory.StartNew (() =>
{
// simulate some work being done
Thread.Sleep (1000);
});
task.Wait ();
}
我希望这种方法有效,但在所有任务完成之前不会更新UI
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;
}
更新/澄清
*我的任务无法并行运行 *我想告知用户每一步都在进行工作 *任务运行时不会发生其他UI输入。
任务不会集中到特定的代码段,而是遍布整个地方。对不起,我不确定如何描述我的情况。
答案 0 :(得分:2)
在此代码示例中,button1_Click
同步运行并将阻止UI线程直到它完成,因此即使在它调用的方法中创建的任务想要更新UI线程,我怀疑这些尝试是排队等到一切都完成后。在您看来,await和async旨在解决此问题。在此之前,BackgroundWorkers可以用来做同样的事情。我将向您的表单添加BackgroundWorker,将所有代码从button1_Click
移动到BackgroundWorker的DoWork
方法,并在单击Button1时启动后台工作程序。我相信BackgroundWorkers已被弃用,但与.Net 3.5配合使用
如果您不熟悉BackgroundWorker类,请参阅以下快速教程:https://www.dotnetperls.com/backgroundworker
插入Application.DoEvents
以暂停执行button1_Click直到UI可以自行更新是另一种选择,但这种方法存在许多潜在问题。请参阅此主题以获取一长串公平警告:Use of Application.DoEvents()
答案 1 :(得分:0)
当运行多个提供您要显示的进度信息的线程时,您可以按照以下步骤操作:
答案 2 :(得分:-1)
您可以在任务完成后立即使用continueWith
更新线程。
var task1 = new Task(() => Thread.Sleep(1000)); //replace with actual task
var task2 = task1.ContinueWith(t => UpdateUI(msg), CancellationToken.None, TaskContinuationOptions.None, uiScheduler);
task1.Start();