仅当后台操作时间长时显示进度

本文关键字:显示 时间 后台 操作 | 更新日期: 2023-09-27 18:04:25

我正在开发一个c#操作,我想显示一个模态进度对话框,但只有当一个操作将很长(例如,超过3秒)。我在后台线程中执行我的操作。

问题是我事先不知道这个操作是长还是短。

一些软件如IntelliJ有一个定时器方法。如果操作时间超过x,则显示一个对话框。

你认为什么是实现这个的好模式?

    等待带有计时器的UI线程,并在那里显示对话框?
  • 我必须DoEvents()时,我显示的对话框?

仅当后台操作时间长时显示进度

我会这样做:

1)使用BackgroundWorker。

2)在调用RunWorkerAsync方法之前,将当前时间存储在一个变量中

3)在DoWork事件中,您需要调用ReportProgress。在ProgressChanged事件中,检查时间是否超过三秒。如果是,显示对话框

这是一个MSDN的BackgroundWorker的例子:http://msdn.microsoft.com/en-us/library/cc221403(v=vs.95).aspx

注意:总的来说,我同意Ramhound的评论。总是显示进度。但如果你不使用BackgroundWorker,我会开始使用它。这会让你的生活更轻松。

我将在这里使用第一个选择,并进行一些修改:

首先在不同线程中运行可能长时间运行的操作。
然后运行另一个线程,通过一个带超时的wait句柄来检查第一个线程的状态,等待它完成。如果超时触发,则显示进度条。

类似:

private ManualResetEvent _finishLoadingNotifier = new ManualResetEvent(false);
private const int ShowProgressTimeOut = 1000 * 3;//3 seconds

private void YourLongOperation()
{
    ....
    _finishLoadingNotifier.Set();//after finish your work
}
private void StartProgressIfNeededThread()
{
    int result = WaitHandle.WaitAny(new WaitHandle[] { _finishLoadingNotifier }, ShowProgressTimeOut);
    if (result > 1)
    {
        //show the progress bar.
    } 
}

假设您有DoPossiblyLongOperation(), ShowProgressDialog()HideProgressDialog()方法,您可以使用TPL为您做繁重的工作:

var longOperation = new Task(DoPossiblyLongOperation).ContinueWith(() => myProgressDialog.Invoke(new Action(HideProgressDialog)));
if (Task.WaitAny(longOperation, new Task(() => Thread.Sleep(3000))) == 1)
    ShowProgressDialog();

建议非阻塞解决方案,不新建线程:

try
{
   var t = DoLongProcessAsync();
   if (await Task.WhenAny(t, Task.Delay(1000)) != t) ShowProgress();
   await t;
}
finally
{
   HideProgress();
}

我会将进度对话框与后台活动分开,以将我的UI逻辑与应用程序的其余部分分开。因此,序列将是(这基本上与IntelliJ所做的相同):

  1. UI启动后台操作(在BackgroundWorker中)并设置一个X秒的计时器
  2. 当计时器到期时UI显示进度对话框(如果后台任务仍在运行)
  3. 当后台任务完成时,定时器被取消,对话框(如果有的话)被关闭

使用计时器而不是单独的线程更节省资源。

我从Jalal Said的回答中得到了这个想法。我要求需要超时或取消进度显示。我没有向WaitAny传递额外的参数(取消令牌句柄),而是将设计更改为依赖于Task.Delay()

private const int ShowProgressTimeOut = 750;//750 ms seconds
public static void Report(CancellationTokenSource cts)
{
    Task.Run(async () =>
    {
        await Task.Delay(ShowProgressTimeOut);
        if (!cts.IsCancellationRequested)
        {
            // Report progress
        }
    });
}

这样用;

private async Task YourLongOperation()
{
    CancellationTokenSource cts = new CancellationTokenSource();
    try
    {
        // Long running task on background thread
        await Task.Run(() => {
            Report(cts);
            // Do work
            cts.Cancel();
        });
    }
    catch (Exception ex) { }
    finally {cts.Cancel();}
}