仅当后台操作时间长时显示进度
本文关键字:显示 时间 后台 操作 | 更新日期: 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所做的相同):
- UI启动后台操作(在BackgroundWorker中)并设置一个X秒的计时器
- 当计时器到期时UI显示进度对话框(如果后台任务仍在运行)
- 当后台任务完成时,定时器被取消,对话框(如果有的话)被关闭
使用计时器而不是单独的线程更节省资源。
我从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();}
}