1

我远离 Application.DoEvents,因为存在重新进入的问题,并且总是有另一种方法可以避免阻止 UI。我一直喜欢在 Application.Idle 事件中做后台工作,作为多线程的简单替代方案。但是,对于长时间运行的任务,是否可以在 Application.Idle 中使用 DoEvents 循环运行操作?

class LongRunningTask : IDisposable {

    public LongRunningTask(IEnumerable<Tidbit> listOfDataToBeProcessed) {
       Application.Idle += OnIdle;
    
        foreach(Tidbit t in listofDataToBeProcessed) {
            tidbits.Enqueue(t);
        }
    }
    
    // Small pieces of data to process
    Queue<Tidbit> tidbits = new Queue<Tidbit>();
    
    void OnIdle(object sender, EventArgs e) {
        while(tidbits.Count > 0) {
            var tidbit = tasks.Dequeue();
            tidbit.Process();
            // Process any messages that have queued up
            // while the data was being processed
            Application.DoEvents();
        }
    }
    
    public void Dispose() {
        Application.Idle -= OnIdle;
    }
}

这里的逻辑是,在我们调用 DoEvents 的 Application.Idle 事件中,没有消息排队,因此我们应该无法重新输入代码。每个花絮都会被足够快地处理,以至于它不应该显着阻塞消息队列(或 UI)。

那么,这种方法有什么害处或缺点吗?即,在 Idle 事件中调用 DoEvents 是否安全?我确实知道C# 5 将有一个功能来解决这种情况,但是对于我们这些不使用 C# 5 的人(目前我们大多数人),出于普遍的好奇,这是一个好的计划?或者是否有更简单的替代方法来在长时间运行的操作期间处理消息而不诉诸多线程?

请不要以为我认为这是一颗灵丹妙药。我知道它带有与任何异步方法相同的缺陷。我知道一个人必须仍然能够保持他的应用程序处于一致状态。而且我知道有多线程解决方案。我正在避免那些。为什么?因为多线程比较复杂。我不认为这是不切实际的。

4

1 回答 1

1

你在这里所做的看起来很像Cooperative multitasking。这是一个有趣的想法,但我会说你应该真正使用多线程来处理这样的事情。.NET 有很多帮助系统,比如Task.Factory.StartNew()真正让它变得简单的 TPL 库。

我建议不要这样做。

不过要明确一点:我没有明确的理由反对它。与许多有关编程的领域一样:如果它有效,它就有效。但是,您将不应该混合在一起的东西混合在一起,特别是后台任务和 UI 任务。我相信,当您分离后台任务并开始着手使用多线程、线程池、后台任务和 TPL 时,您会很快发现它确实有效,并且从长远来看会使生活更轻松。

于 2010-11-07T17:21:44.970 回答