2011-11-06 52 views
8

我需要一个比Thread.Sleep更好的睡眠任务设计,我使用的是Task类。x时间等待任务并报告进度

在我的WPF应用程序我运行一个任务,这个任务运行另一对夫妇的任务,分别关于该任务第一次登录到和互联网网站,之后便登录他们需要等待几秒钟,并通知用户时,他们将继续更进一步,这是我的问题,因为使用Thread.Sleep我无法报告进度。

还有更多的功能比登录,大约5-6,他们都需要一个互联网资源,并且他们之间需要有剩余时间的睡眠时间报告发送gui,这5-6功能都是在一个任务中,但可能有很多任务。

我需要做的是让任务等待,也让它发送的剩余时间更新进行到GUI。

你有这个问题的任何想法,如何使它更好,也许你有一些设计模式已经为这样的问题?

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

编辑:没有人知道? 什么是某种自带定时器的线程等待waithandle,autoresetevent,任何人?

+1

我有一个很好的回答这个问题一段时间回来: http://stackoverflow.com/questions/7311468/best-practice-for-thread-delays-with-countdown-notification – drharris

+0

THX,非常有帮助。 – Programista

回答

1

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

你的后台线程可以发送更新与异步委托到用户界面,但直到事情发生调用线程将等待它:

1)你的线程报告已通过完成任务使用yourwaithandle.Set();

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

+0

是的,我从一开始就在想同样的事情,但没有人指出这个方向,为什么我也不知道链接形式drharris是非常有帮助的,所以我会使一些lo根据waithandle或重置事件和定时器,我自己的gic。 – Programista

1

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

随着每个功能的完成,它会对下一个要执行的功能进行排队。所以,如果前两个功能是登录和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次(或有东西时被添加到队列中的计时器的滴答旁边时更新)。它可以根据需要开始个别任务。

新的异步CTP可能有这样的事情已经:一个“在一次执行任务”。可能值得研究它。

对于报告进度,每个函数都可以在启动时报告进度(即“登录”...“等待5秒钟获取数据”...“获取数据”等)如果你想,你可以使定时器线程定期走过优先级队列,并报告何时执行特定的任务。虽然这可能是矫枉过正。

,你听到什么是正确的:Thread.Sleep是一个非常糟糕的主意,尤其是当你与线程池中的线程工作。

+0

这不是一个好的解决方案,即使在一个多线程应用程序中你需要一个结构化代码,每个任务中的5-6个函数都是好的,它们都是相关的,它会让每个函数在一个单独的任务中变得一团糟,每个任务都是一个相当大的代码,我不认为排队功能是一个真正的解决方案,我正在寻找更好的东西,没有什么不对,这些功能是在一起完成的任务,这段代码很好,但睡眠部分没有。 我拒绝认为没有更好的办法。它不能“等待5秒钟”,它必须是一个真正的倒计时,并且定期监测有点“黑客”。 – Programista

+0

@Programista:关键是延迟线程的唯一方法就是睡觉。而且由于睡眠不好,你必须想出其他方式来解决问题。 –

+0

这是不正确的,任务可以通过其他功能停止,但我需要已经做了它的人告诉我如何,特别是剩余时间更新。 – Programista

6

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

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

这也似乎是一个理想的使用情况下的数据流或接收。

利用无扩展:

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 
+0

有趣的是,我不能使用异步CTP,因为它是一个生产就绪的商业应用程序,但我会用一些辅助类来检查这个选项的正常方式。 在内部定时器计数的自包含阻塞情况下,您对某些自动或手动重置事件有何看法? – Programista

+0

@Programista Blocking不是解决方案。在这种情况下你真正需要的是回调。就像使用Continuation Passing Style一样。如果您有兴趣,我会编辑答案以包含使用Microsoft Reactive Extensions(Rx)的解决方案。 – Asti

+0

我不知道Rx,所以这会帮助我。 – Programista

0

你有没有考虑使用定时器(从System.Timers)来执行间隙“剩余时间“的消息更新?您的每个任务都可以设置预期的完成时间值,然后您的计时器任务可以负责按需要对UI进行倒计时和更新。不需要Thread.Sleep,Timer将在线程池线程上执行它的Tick代码。

+0

是的,但只是作为一个内部参考,在一些情况下阻塞线程和使用计时器作为滴答计数,然后计数滴答(滴答可能是100ms,1秒等)。基于方法)并且每秒用剩余时间向ui发送更新,如果时间变为零,则释放线程并且处理自包含的块线程count&update ui。 – Programista

+0

我正在谈论一个全球总体计时器,可以监控各个任务的进度。我认为你在个别任务中试图做得太多;如果任务的结束也处理UI,那为我提出一个红旗。 –

+0

不,不能,它不能用作监视器,任务本身应该被阻塞例如3秒,但是我需要在ui“wait 3..2..1”秒中显示,等等。也许一个计时器可以作为一个全球100毫秒的时间参考为其他对象的倒计时目的,并且ui不处理,我的意思是自我包含单独的对象/类与逻辑阻塞线程,计数剩余时间和更新的用户界面处置当时间到了0和线程被释放。 – Programista