假设我有一个方法,我通过Task.Factory.StartNew()
在一个单独的线程中运行
此方法报告了很多进度(IProgress
),它冻结了我的GUI。
我知道简单地减少报告数量就是一个解决方案,比如仅报告十分之一,但在我的情况下,我真的想要获取所有报告并在我的GUI中显示它们。
我的第一个想法是将所有报告排队并逐一处理,暂停每一报告。
首先:这是一个不错的选择吗?
其次:如何实现?使用计时器或使用某种Task.Delay()
?
更新
我会试着更好地解释一下。发送到GUI的进度包括我在地图上显示的地理坐标。一个接一个地显示每个进度在地图上提供某种动画。这就是为什么我不想跳过它们中的任何一个。
实际上,我不介意我在另一个线程中执行的方法是否在动画之前完成。我想要的是确保所有点都显示至少一段时间(比方说200毫秒)。
答案 0 :(得分:2)
如果这是结果,那么在单独的线程中运行进程的整个过程就会被浪费。因此,我的第一个建议是尽可能减少更新次数。
如果这是不可能的,也许你可以修改你发送的数据作为每次更新的一部分。用于报告的对象或数据结构有多大,有多复杂?可以通过降低其复杂性来提高性能吗?
最后,您可以尝试另一种方法:如果您创建仅处理报告的第三个线程,并以更大的块将其传递到GUI,该怎么办?如果你让你的工作线程向这个记者线程报告它的状态,那么让记者线程仅偶尔向你的主GUI线程报告(例如,每10个中,你自己在上面建议,然后报告10个块数据一次),那么你不会经常调用你的GUI,但你仍然可以保留处理中的所有状态数据,并使其在GUI中可用。
我不知道这对你的特殊情况有多可行,但它可能值得一两个实验?
答案 1 :(得分:1)
我对您的解决方案有很多疑虑,但我无法确定哪一个在没有代码示例的情况下可能会出现问题。
首先,Stephen Cleary在他的StartNew
is Dangerous文章中指出了使用默认参数的方法存在的真正问题:
对于简单的案例来说足够简单,但让我们考虑一个更现实的例子:
private void Form1_Load(object sender, EventArgs e) { Compute(3); } private void Compute(int counter) { // If we're done computing, just return. if (counter == 0) return; var ui = TaskScheduler.FromCurrentSynchronizationContext(); Task.Factory.StartNew(() => A(counter)) .ContinueWith(t => { Text = t.Result.ToString(); // Update UI with results. // Continue working. Compute(counter - 1); }, ui); } private int A(int value) { return value; // CPU-intensive work. }
... 现在,问题返回:
A
运行什么线程?来吧,走过去吧;你应该在这一点上有足够的知识来找出答案 准备? 方法A
第一次在线程池线程上运行,然后在最后两次在UI
线程上运行。
我强烈建议您阅读整篇文章,以便更好地理解StartNew
方法用法,但是想要指出最后的建议:
不幸的是,
StartNew
唯一需要重载的重载TaskScheduler
还要求您指定CancellationToken
和TaskCreationOptions
。这意味着要使用Task.Factory.StartNew
到可靠,可预测地将工作排到了 线程池,你必须使用这样的重载:Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
真的,这有点荒谬。只需使用
Task.Run(() => A());
。
因此,只需切换您正在创建新闻任务的方法,就可以轻松改进您的代码。但是对于你的问题还有其他一些建议:
BlockingCollection
来存储报告,并将一个简单的消费者从此队列写入UI,因此您总是需要有限数量的报告来表示,但最后所有报告都将被处理。 ConcurrentExclusiveSchedulerPair
class作为您的逻辑:要生成报告,请使用ConcurrentScheduler
Property并使用ExclusiveScheduler
Property显示报告。