【问题标题】:Wait task for x amount of time and report progress等待任务 x 时间并报告进度
【发布时间】:2011-11-06 19:17:21
【问题描述】:

我需要一个比 Thread.Sleep 更好的睡眠任务设计,我正在使用 Task 类。

在我的 wpf 应用程序中,我运行一个任务,该任务运行另外几个任务,每个任务首先登录到 Internet 网站,登录后他们需要等待几秒钟并通知用户他们何时会继续进行这是我的问题,因为使用 Thread.Sleep 我无法报告进度。

还有比登录更多的功能,大概有5-6个,都是请求网络资源,而且它们之间需要有一个sleep time 和report send do gui,这5-6个函数都在一个任务,但可能有很多任务。

我需要让任务等待,但也允许它在剩余时间发送更新以继续到 GUI。

你对这个问题有什么想法,如何让它变得更好,也许你已经有一些设计模式来解决这个问题?

我还听说使用 Thread.Sleep 是一种糟糕的设计实践。

编辑:没人知道吗? 像waithandle、autoresetevent 等带有线程等待的某种自包含定时器是怎么回事?

【问题讨论】:

标签: .net wpf task wait progress


【解决方案1】:

解决方案是将其视为异步回调,而不是同步等待。 如果您使用 Async CTP,正确的方法是:

async item => { await Task.Delay(1000); Process(item); }

这似乎也是 DataFlow 或 Rx 的理想用例。

使用响应式扩展:

static void Track(int timeout, int frequency, string item)
    {
        Observable.Interval(TimeSpan.FromSeconds(frequency)) //produces 0, 1, 2.. with the interval
                  .Do(i => Console.WriteLine("Working on {0}", item)) // work on item
                  .TakeUntil(Observable.Timer(TimeSpan.FromSeconds(timeout))) //stop once the timer publishes a value
                  .Subscribe
                  (
                        i => Console.WriteLine("Reporting {0}%", ((double)(i + 1) / timeout * 100)), // the interval reaches OnNext
                        e => Console.WriteLine("Error!"), // error occured 
                        () => Console.WriteLine("Completed") // observable completed
                  );
    }

使用Track(timeout: 5, frequency: 1, item: "http://example.com/?") 调用它时,产生的输出是:

Working on http://example.com/?
Reporting 20%
Working on http://example.com/?
Reporting 40%
Working on http://example.com/?
Reporting 60%
Working on http://example.com/?
Reporting 80%
Completed

【讨论】:

  • 有趣,我不能使用 Async CTP,因为它是一个生产就绪的商业应用程序,但我将使用一些帮助程序类以正常方式检查该选项。在具有内部计时器计数的自包含阻塞场景中,您对某些自动或手动重置事件有何看法?
  • @Programista 阻止不是解决方案。在这种情况下,您真正​​需要的是回调。就像使用继续传递样式一样。如果您有兴趣,我将编辑答案以包含使用 Microsoft 的 Reactive Extensions (Rx) 的解决方案。
  • 我不知道 Rx,所以这对我有帮助。
  • @Programista 我添加了一个使用 Rx 解决此问题的示例。这是对流的另一种思考方式。
  • 不幸的是,在与同事会面后,我们决定不使用任何非生产就绪库,如 async ctp,也不使用其他库仅用于一个简单的功能 (Rx)。
【解决方案2】:

忘记使用 Thread.Sleep。相反,在后台线程中运行您的任务并使用带有 AutoResetEvent 的 WaitHandle。 (链接:WaitHandle/WaitOne/AutoReset

您的后台线程可以使用异步委托向 UI 发送更新,但调用线程将等待它,直到任何事情发生:

1) 您的线程使用 yourwaithandle.Set() 报告它已完成任务;

2) 等待线程超时(超时值设置为 WaitOne() 方法的参数)。

【讨论】:

  • 是的,我从一开始就在想同样的事情,但没有人指向这个方向,为什么我也不知道链接表格 drharris 非常有帮助,所以我会自己做一些逻辑,基于等待句柄或重置事件和计时器。
【解决方案3】:

与其让一个任务执行所有这些六个功能(登录等),不如让每个功能成为一个单独的任务。然后,您可以创建一个按时间排序的优先级队列。当您想要启动其中一个元任务时,您将其第一个函数与状态数据(即 url、用户 ID、密码等)一起放入队列中。

当每个功能完成时,它会将下一个要执行的功能排入队列。因此,如果前两个函数是 login 和 get_data,您将拥有:

queue login with state data to execute at DateTime.Now
When login is finished, it queues get_data with the state data and DateTime.Now + 5 seconds (or whatever time)

当最后一个函数执行时,它会将结果发布到某处。

然后,您可以设置一个计时器,该计时器将每秒轮询队列 10 次(或者在将某些内容添加到队列时更新计时器的下一个滴答时间)。它可以根据需要启动单个任务。

新的 Async CTP 可能已经有了这种东西:“定时执行任务”。可能值得研究一下。

关于报告进度,每个功能都可以在启动时报告进度(即“登录”......“等待5秒获取数据”......“获取数据”等)如果你愿意,你可以使计时器线程定期遍历优先级队列并报告何时执行特定任务。虽然这可能有点矫枉过正。

您听到的是正确的:Thread.Sleep 是一个非常糟糕的主意,尤其是当您使用线程池线程时。

【讨论】:

  • 这不是一个好的解决方案,即使在多线程的应用程序中你也需要一个结构化的代码,每个Task中5-6个函数都很好,它们都是相关的,每个函数都弄得一团糟在单独的任务中,每个任务都是一大段代码,我不认为排队功能是一个真正的解决方案,我正在寻找更好的东西,这些功能在一个任务中并没有错,这段代码很好但睡眠部分不是。我拒绝认为没有更好的方法。不能是“等待 5 秒”,必须是真正的倒计时,并且定期监控有点“黑客”。
  • @Programista:关键是延迟线程的唯一方法是休眠。而且由于睡眠不好,您必须想出一些其他方法来解决问题。
  • 这不是真的,任务可以被其他功能停止,但我需要已经完成它的人告诉我如何,尤其是剩余时间更新。
【解决方案4】:

您是否考虑过使用计时器(来自 System.Timers)来执行插页式“剩余时间”消息更新?您的每个任务都可以设置预期的完成时间值,然后您的计时器任务可以负责倒计时并根据需要更新 UI。不需要 Thread.Sleep,Timer 将在线程池线程上执行其 Tick 代码。

【讨论】:

  • 是的,但仅作为内部参考,在某些情况下阻塞线程并使用计时器作为滴答计数,然后对滴答进行计数(根据方法,滴答可以是 100 毫秒、1 秒等)并且每秒向ui发送更新剩余时间,如果时间变为零,则释放线程并释放自包含的块线程,计数和更新ui。
  • 我说的是一个全局整体计时器,它可以监控各个任务的进度。我认为您在单个任务中尝试做太多事情;如果任务结束时也释放 UI,这对我来说是一个危险信号。
  • 不,不,它不能作为监视器工作,任务本身应该被阻止例如 3 秒,但我需要在 ui 中显示“等待 3..2..1”第二个等等。也许一个计时器可以用作全局 100 毫秒时间参考,用于其他对象的倒计时目的,并且 ui 没有被释放,我的意思是自包含的单个对象/类,其逻辑阻止线程、计算剩余时间和更新的 ui 被释放当时间到 0 时线程被释放。
  • 一个线程不能同时被阻塞和更新 UI。您需要以不同的方式构建您的软件。或者可能是这样的:
  • oops... auto-submit :( 线程 A 是您的工作线程,线程 B 是您的 UI 更新线程。线程 A 确实会阻塞,而线程 B 在计时器上并更新UI。在每次更新 UI 之前,线程 B 会检查来自线程 A 的信号,表明其工作已完成,如果线程 A 已完成,则终止自己。不过,底线是您至少需要两个线程才能完成你在说什么。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多