简体   繁体   English

延迟执行的最佳方法

[英]Best way to delay execution

Let's say I have a method that I run in a separate thread via Task.Factory.StartNew() . 假设我有一个方法,我通过Task.Factory.StartNew()在一个单独的线程中运行。
This method reports so many progress ( IProgress ) that it freezes my GUI. 这个方法报告了很多进展( IProgress ),它冻结了我的GUI。 I know that simply reducing the number of reports would be a solution, like reporting only 1 out of 10 but in my case, I really want to get all reports and display them in my GUI. 我知道简单地减少报告数量就像一个解决方案,就像只报告十分之一一样,但在我的情况下,我真的想要获取所有报告并在我的GUI中显示它们。

My first idea was to queue all reports and treat them one by one, pausing a little bit between each of them. 我的第一个想法是将所有报告排队并逐一处理,在每个报告之间暂停一点。

Firstly: Is it a good option? 首先:这是一个不错的选择吗?

Secondly: How to implement that? 第二:如何实现? Using a timer or using some kind of Task.Delay() ? 使用计时器或使用某种Task.Delay()

UPDATE: 更新:
I'll try to explain better. 我会试着更好地解释一下。 The progress sent to the GUI consists of geocoordinates that I display on a map. 发送到GUI的进度包括我在地图上显示的地理坐标。 Displaying each progress one after another provide some kind of animation on the map. 一个接一个地显示每个进度在地图上提供某种动画。 That's why I don't want to skip any of them. 这就是为什么我不想跳过任何一个。

In fact, I don't mind if the method that I execute in another thread finishes way before the animation. 事实上,我不介意我在另一个线程中执行的方法是否在动画之前完成。 All I want, is to be sure that all points have been displayed for at least a certain amount of time (let's say 200 ms). 我想要的是确保所有点都已显示至少一段时间(比方说200毫秒)。

Sounds like the whole point of having the process run in a separate thread is wasted if this is the result. 如果这是结果,那么在单独的线程中运行进程的整个过程就会被浪费。 As such, my first recommendation would be to reduce the number of updates if possible. 因此,我的第一个建议是尽可能减少更新次数。

If that is out of the question, perhaps you could revise the data you are sending as part of each update. 如果这是不可能的,也许你可以修改你发送的数据作为每次更新的一部分。 How large, and how complex is the object or data-structure used for reporting? 用于报告的对象或数据结构有多大,有多复杂? Can performance be improved by reducing it's complexity? 通过降低其复杂性可以改善性能吗?

Finally, you might try another approach: What if you create a third thread that just handles the reporting, and delivers it to your GUI in larger chunks? 最后,您可以尝试另一种方法:如果您创建仅处理报告的第三个线程,并以更大的块将其传递到GUI,该怎么办? If you let your worker-thread report it's status to this reporter-thread, then let the reporter thread report back to your main GUI-thread only occasionally (eg every 1 in 10, as you suggest yourself above, bur then reporting 10 chunks of data at once), then you won't call on your GUI that often, yet you'll still be able to keep all the status data from the processing, and make it available in the GUI. 如果你让你的工作线程向这个记者线程报告它的状态,那么让记者线程仅偶尔向你的主GUI线程报告(例如,每10个中,你自己在上面建议,然后报告10个块数据一次),那么你不会经常调用你的GUI,但你仍然可以保留处理中的所有状态数据,并使其在GUI中可用。

I don't know how viable this will be for your particular situation, but it might be worth an experiment or two? 我不知道这对你的特殊情况有多可行,但它可能值得一两个实验?

I have many concerns regarding your solution, but I can't say for sure which one can be a problem without code samples. 我对您的解决方案有很多疑虑,但我不能肯定地说没有代码示例哪一个可能是一个问题。

First of all, Stephen Cleary in his StartNew is Dangerous article points out the real problem with this method with using it with default parameters: 首先,Stephen Cleary在他的StartNew中使用Dangerous文章指出了使用默认参数的方法的真正问题:

Easy enough for the simple case, but let's consider a more realistic example: 对于简单的情况来说足够简单,但让我们考虑一个更现实的例子:

 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. } 

... Now, the question returns: what thread does A run on? ...现在,问题返回: A运行什么线程? Go ahead and walk through it; 来吧,走过去吧; you should have enough knowledge at this point to figure out the answer. 你应该在这一点上有足够的知识来找出答案。
Ready? 准备? The method A runs on a thread pool thread the first time, and then it runs on the UI thread the last two times. 方法A第一次在线程池线程上运行,然后在最后两次在UI线程上运行。

I strongly recommend you to read whole article for better understanding the StartNew method usage, but want to point out the last advice from there: 我强烈建议您阅读整篇文章以更好地理解StartNew方法的用法,但是想要指出最后的建议:

Unfortunately, the only overloads for StartNew that take a TaskScheduler also require you to specify the CancellationToken and TaskCreationOptions . 不幸的是,使用TaskScheduler StartNew的唯一重载也要求您指定CancellationTokenTaskCreationOptions This means that in order to use Task.Factory.StartNew to reliably, predictably queue work to the thread pool , you have to use an overload like this: 这意味着为了使用Task.Factory.StartNew 可靠,可预测地将工作排队到线程池 ,您必须使用如下的重载:

 Task.Factory.StartNew(A, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default); 

And really, that's kind of ridiculous. 真的,这有点荒谬。 Just use Task.Run(() => A()); 只需使用Task.Run(() => A()); .

So, may be your code can be easily improved simply by switching the method you are creating news tasks. 因此,只需切换您正在创建新闻任务的方法,就可以轻松改进您的代码。 But there is some other suggestions regarding your question: 但是对于你的问题还有其他一些建议:

  1. Use BlockingCollection for the storing the reports, and write a simple consumer from this queue to UI, so you'll always have a limited number of reports to represent, but at the end all of them will be handled. 使用BlockingCollection存储报告,并将一个简单的使用者从此队列写入UI,因此您将始终只有有限数量的报告来表示,但最后将处理所有报告。
  2. Use a ConcurrentExclusiveSchedulerPair class for your logic: for generating the reports use the ConcurrentScheduler Property and for displaying them use ExclusiveScheduler Property . 对您的逻辑使用ConcurrentExclusiveSchedulerPair :要生成报告,请使用ConcurrentScheduler属性并使用ExclusiveScheduler属性显示它们。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM