mirror of
https://github.com/aphrody-labs/PowerShell.git
synced 2026-10-17 12:45:47 +00:00
Improve a progress pane performance (one thread) (#2822)
Showing a progress bar is handled on the pipeline thread only. But not every "WriteProgress" will trigger the "_progPane.Show" to be called. Instead, a timer is used to set an "update" flag that will be checked in "HandleIncomingProgressRecord" to see whether "_progPane.Show" should be called. The timer fires every 200 ms. I test intervals from 50 ms to 2000 ms and I didn't measure much difference with 100 ms and 200 ms, so 200 ms is chosen to make it more cost-efficient.
This commit is contained in:
1 parent
3a21d4c3df
commit
915dfcd0d9
1 file changed
+59
-7
@@ -7,6 +7,7 @@ Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
using System;
|
||||
using System.Management.Automation;
|
||||
using Dbg = System.Management.Automation.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
|
||||
namespace Microsoft.PowerShell
|
||||
@@ -28,14 +29,30 @@ namespace Microsoft.PowerShell
|
||||
// destroy the data structures representing outstanding progress records
|
||||
// take down and destroy the progress display
|
||||
|
||||
if (_progPane != null)
|
||||
// If we have multiple runspaces on the host then any finished pipeline in any runspace will lead to call 'ResetProgress'
|
||||
// so we need the lock
|
||||
lock (_instanceLock)
|
||||
{
|
||||
Dbg.Assert(_pendingProgress != null, "How can you have a progress pane and no backing data structure?");
|
||||
if (_progPaneUpdateTimer != null)
|
||||
{
|
||||
// Stop update a progress pane and destroy timer
|
||||
_progPaneUpdateTimer.Dispose();
|
||||
_progPaneUpdateTimer = null;
|
||||
}
|
||||
// We don't set 'progPaneUpdateFlag = 0' here, because:
|
||||
// 1. According to MSDN, the timer callback can occur after the Dispose() method has been called.
|
||||
// So we cannot guarantee the flag is truly set to 0.
|
||||
// 2. When creating a new timer in 'HandleIncomingProgressRecord', we will set the flag to 1 anyway
|
||||
|
||||
_progPane.Hide();
|
||||
_progPane = null;
|
||||
if (_progPane != null)
|
||||
{
|
||||
Dbg.Assert(_pendingProgress != null, "How can you have a progress pane and no backing data structure?");
|
||||
|
||||
_progPane.Hide();
|
||||
_progPane = null;
|
||||
}
|
||||
_pendingProgress = null;
|
||||
}
|
||||
_pendingProgress = null;
|
||||
}
|
||||
|
||||
|
||||
@@ -64,15 +81,46 @@ namespace Microsoft.PowerShell
|
||||
|
||||
if (_progPane == null)
|
||||
{
|
||||
// This is the first time we've received a progress record. Create a progress pane, then show it.
|
||||
// This is the first time we've received a progress record
|
||||
// Create a progress pane
|
||||
// Set up a update flag
|
||||
// Create a timer for updating the flag
|
||||
|
||||
_progPane = new ProgressPane(this);
|
||||
|
||||
if (_progPaneUpdateTimer == null)
|
||||
{
|
||||
// Show a progress pane at the first time we've received a progress record
|
||||
progPaneUpdateFlag = 1;
|
||||
|
||||
// The timer will be auto restarted every 'UpdateTimerThreshold' ms
|
||||
_progPaneUpdateTimer = new Timer( new TimerCallback(ProgressPaneUpdateTimerElapsed), null, UpdateTimerThreshold, UpdateTimerThreshold);
|
||||
}
|
||||
}
|
||||
|
||||
if (Interlocked.CompareExchange(ref progPaneUpdateFlag, 0, 1) == 1 || record.RecordType == ProgressRecordType.Completed)
|
||||
{
|
||||
// Update the progress pane only when the timer set up the update flag or WriteProgress is completed.
|
||||
// As a result, we do not block WriteProgress and whole script and eliminate unnecessary console locks and updates.
|
||||
_progPane.Show(_pendingProgress);
|
||||
}
|
||||
_progPane.Show(_pendingProgress);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// TimerCallback for '_progPaneUpdateTimer' to update 'progPaneUpdateFlag'
|
||||
///
|
||||
/// </summary>
|
||||
|
||||
private
|
||||
void
|
||||
ProgressPaneUpdateTimerElapsed(object sender)
|
||||
{
|
||||
Interlocked.CompareExchange(ref progPaneUpdateFlag, 1, 0);
|
||||
}
|
||||
|
||||
private
|
||||
void
|
||||
PreWrite()
|
||||
@@ -166,6 +214,10 @@ namespace Microsoft.PowerShell
|
||||
|
||||
private ProgressPane _progPane = null;
|
||||
private PendingProgress _pendingProgress = null;
|
||||
// The timer set up 'progPaneUpdateFlag' every 'UpdateTimerThreshold' milliseconds to update 'ProgressPane'
|
||||
private Timer _progPaneUpdateTimer = null;
|
||||
private const int UpdateTimerThreshold = 200;
|
||||
private int progPaneUpdateFlag = 0;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
|
||||
Reference in new issue
Block a user