Я открываю новое подокно из основного при нажатии кнопки. После загрузки подокно запускает задачу с отслеживанием положения курсора мыши. При закрытии подокна сигнальной переменной stopMouseTracker
присваивается значение true
, и бесконечный цикл в задаче должен завершиться. Код ниже работает как в режиме отладки, так и в режиме выпуска без оптимизации кода. Но когда я запускаю свой код в Release с включенной оптимизацией кода, подокно зависает после того, как я пытаюсь его закрыть. Что может вызвать проблему?
Главное окно:
public MainWindow()
{
InitializeComponent();
}
private void button_Click(object sender, RoutedEventArgs e)
{
SubWindow subWindow = new();
subWindow.ShowDialog();
}
Подокно:
bool mouseTrackerOn = false;
bool stopMouseTracker = false;
public SubWindow()
{
InitializeComponent();
StartMouseTracker();
}
private void StartMouseTracker()
{
mouseTrackerOn = true;
Task.Factory.StartNew(() =>
{
while (!stopMouseTracker)
{
var p = System.Windows.Forms.Cursor.Position;
// Write cursor coordinates to text box tbXY
tbXY.Dispatcher.BeginInvoke(() => tbXY.Text = $"{p.X} {p.Y}");
Thread.Sleep(1);
}
mouseTrackerOn = false;
});
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
stopMouseTracker = true;
// This blocks the thread completely in Release with code optimization on
while (mouseTrackerOn) ;
}
@Perotto: Более глубокая причина заключается в том, что переменная была встроена в регистр, где другой поток никогда больше не получит доступ к этой ячейке памяти, что приводит к наблюдаемому вами бесконечному циклу.
У вас есть незаблокированная переменная, которую вы проверяете из другого потока. Хотя volatile
или Interlocked.Read
и т. д. исправят это, вам все равно не нужна вся эта возня с задачами и Dispatcher
.
Просто используйте CancellationToken
, чтобы прервать цикл, и поместите цикл в поток пользовательского интерфейса, используя async
.
private CancellationTokenSource _stopMouseTracker = new();
public SubWindow()
{
InitializeComponent();
StartMouseTracker(_stopMouseTracker.Token);
}
private async Task StartMouseTracker(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
var p = System.Windows.Forms.Cursor.Position;
// Write cursor coordinates to text box tbXY
tbXY.Text = $"{p.X} {p.Y}";
await Task.Delay(1, cancellationToken);
}
}
catch
{ //
}
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
stopMouseTracker.Cancel();
}
Я бы предпочел использовать System.Windows.Threading.DispatcherTimer
или System.Threading.PeriodicTimer
, ни один из которых не требует CancellationTokenSource
и проглатывания исключений.
Я вас слышу, думаю, единственное исключение, которого вы ожидаете, это OperationCanceled
. Кнопка Ответить, кстати, находится внизу.
Charlieface Я не склонен отвечать на этот вопрос. Вероятно, существует полдюжины дубликатов, связанных с эффектом пропуска volatile
в логическом флаге, и мне сейчас лень их искать.
попробуйте сделать переменные bool «изменчивыми»