Сильно различающиеся быстродействие цикла, с отладкой и без
Минимальный код для воспроизведения проблемы.
internal partial class Win32WPFWindowPositionUpdater
{
private readonly Window _window;
private readonly Dispatcher _dispatcher;
private const int IGNORE_SIZE_WINDOW = -1;
private const int HWND_TOP = 0;
private const int NOT_MESSAGE_WM_WINDOWPOSCHANGING = 0x0400;
private const int SWP_NOSIZE = 0x0001;
private const int OFFSET_CURSOR = 30;
public Win32WPFWindowPositionUpdater(Window window)
{
_window = window;
_dispatcher = _window.Dispatcher;
}
public Task UpdateWindowPos(CancellationToken token) => UpdateWindowPositionRelativeToCursor(token);
[LibraryImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool SetWindowPos(IntPtr handle, int handle2, int x, int y, int cx, int cy, int flag);
private async Task UpdateWindowPositionRelativeToCursor(CancellationToken token)
{
WindowInteropHelper helper = new WindowInteropHelper(_window);
while (token.IsCancellationRequested is not true)
{
await Task.Delay(1, token);
if (token.IsCancellationRequested is true) return;
Point point = GetCursorPosition();
await _window.Dispatcher.InvokeAsync(() =>
{
SetWindowPos(helper.Handle, HWND_TOP, Convert.ToInt32(point.X - OFFSET_CURSOR), Convert.ToInt32(point.Y - OFFSET_CURSOR),
IGNORE_SIZE_WINDOW, IGNORE_SIZE_WINDOW, NOT_MESSAGE_WM_WINDOWPOSCHANGING | SWP_NOSIZE);
}, System.Windows.Threading.DispatcherPriority.Render, token);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
public int X;
public int Y;
public static implicit operator Point(POINT point)
{
return new Point(point.X, point.Y);
}
}
[LibraryImport("user32")]
[return: MarshalAs(UnmanagedType.Bool)]
private static partial bool GetCursorPos(out POINT lpPoint);
internal static Point GetCursorPosition()
{
GetCursorPos(out POINT lpPoint);
return lpPoint;
}
}
}
public partial class MainWindow : Window
{
private readonly Win32WPFWindowPositionUpdater _positionUpdater;
private readonly CancellationTokenSource _tokenSource;
public MainWindow()
{
InitializeComponent();
_positionUpdater = new Win32WPFWindowPositionUpdater(this);
_tokenSource = new CancellationTokenSource();
_ = new Timer(new TimerCallback((_) =>
{
_positionUpdater.UpdateWindowPos(_tokenSource.Token);
}), null, 1500, 0);
_ = new Timer(new TimerCallback((_) =>
{
_tokenSource.Cancel();
}), null, 7000, 0);
}
}
Визуально видно, что окно без отладки обновляет позицию окна медленнее. Если точнее, то вычисленная мною задержка между итерациями цикла обновления окна падает до 29% от изначальной. Причем на это влияет опция "горячая перезагрузка XAML", при её отключении в настройках Visual Studio. Скорость выполнения также падает.
Ожидаемое поведение - незначительные отклонения скорости выполнения цикла между режимами, с отладкой и без.
Это воспроизводимое поведение, или это проблемы на моём локальном компьютере? Если воспроизводимое, то по возможности разобраться в причинах, и устранить разницу.
UPDATE:
Будет выполнятся около 15 секунд.
internal class Program
{
static async Task Main()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
await MyTask();
stopwatch.Stop();
await Console.Out.WriteLineAsync($"{stopwatch.Elapsed}");
}
private static async Task<int> MyTask()
{
int count = 0;
while (count < 1000)
{
await Task.Delay(1);
count++;
await Console.Out.WriteLineAsync($"{count}");
}
return count;
}
}
Это около 2-3 секунд.
internal class Program
{
static async Task Main()
{
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
await MyTask();
stopwatch.Stop();
await Console.Out.WriteLineAsync($"{stopwatch.Elapsed}");
}
private static async Task<int> MyTask()
{
Thread thread = new Thread(() =>
{
System.Windows.Window window = new System.Windows.Window();
System.Windows.Application application = new System.Windows.Application();
application.Run(window);
});
thread.SetApartmentState(ApartmentState.STA);
thread.Start();
int count = 0;
while (count < 1000)
{
await Task.Delay(1);
count++;
await Console.Out.WriteLineAsync($"{count}");
}
return count;
}
}
Когда NET 7 и "горячая перезагрузка" включены.
Потому что приложение запросило таймер при await Task.Delay(1)
в этом случаи, в отличии от первого. Убедится в этом можно прописав powercfg -energy duration 1
в консоль, прочить файл отчёта.
UPDATE:
Оказывается недостаточно просто указать await Task.Delay(1)
, на самом деле в большинстве случаев будет просто использоваться значение в "15.625 ms".
Чтобы использовать меньшие величины нужен явный вызов timeBeginPeriod
P.S. В принце об этой задержки невнятно в документации по Task.Dalay() написано.