C# 仅当后台操作较长时才显示进度

C# 仅当后台操作较长时才显示进度,c#,.net,winforms,background,progress,C#,.net,Winforms,Background,Progress,我正在开发一个C#操作,我想显示一个模式进度对话框,但只有当操作很长时(例如,超过3秒)。我在后台线程中执行操作 问题是我事先不知道手术是长还是短 有些软件有一个计时器。如果操作花费的时间超过x,则显示一个对话框 您认为实现这一点的好模式是什么 使用计时器等待UI线程,并在那里显示对话框 显示对话框时必须DoEvents() 我将在这里选择第一个选项,并进行一些修改: 首先在不同的线程中运行可能的长时间运行操作。 然后运行一个不同的线程来检查第一个线程的状态,通过一个带有超时的等待句柄来等待它

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

问题是我事先不知道手术是长还是短

有些软件有一个计时器。如果操作花费的时间超过x,则显示一个对话框

您认为实现这一点的好模式是什么

  • 使用计时器等待UI线程,并在那里显示对话框
  • 显示对话框时必须
    DoEvents()

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

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

比如:

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.
    } 
}
下面是我要做的:

1) 使用后台工作人员

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

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

以下是BackgroundWorker的MSDN示例:


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

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

  • UI启动后台操作(在BackgroundWorker中)并设置一个计时器,时间为X秒
  • 计时器过期时,UI显示进度对话框(如果后台任务仍在运行)
  • 后台任务完成后,计时器被取消,对话框(如果有)关闭

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

    假设您有
    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();
    }
    
    我从答案中得到了这个想法。我需要超时或取消进度显示。我没有向
    WaitAny
    传递额外的参数(取消令牌句柄),而是将设计更改为依赖于
    Task.Delay()

    像这样使用它

    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();}
    }
    

    你为什么不直接显示进度呢。如果任务很短,则进度条将被填充,并且只显示很短的时间。例如,Office总是在底部显示加载进度条,即使在一个小文档上也是如此。当操作速度很快时,看到闪烁的对话框(毫秒)是很烦人的。我建议使用Task.Sleep,而不是Task.Delay,以免使整个线程进入睡眠状态(多个任务可能在同一线程上运行)。@MCattle:True。在撰写此答案时,框架中不存在Task.Delay,使用System.Threading.Timer会使事情变得太复杂,无法演示我试图演示的简单原理。
    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();}
    }