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