Почему поток не присоединяется при закрытии приложения? - PullRequest
0 голосов
/ 08 февраля 2019

Я сделал приложение WPF с индикатором выполнения.Значение индикатора выполнения обновляется до случайного значения в цикле «while» в функции «Background_Work».Эта функция выполняется в отдельном потоке.Если я закрываю окно, я хочу завершить функцию (путем завершения цикла while) и присоединиться к потоку.

Проблема в том, что в некоторых случаях окно зависает и не закрывается.

    public partial class MainWindow : Window
    {
        Random random;
        Thread background_work;
        bool running;

        public MainWindow()
        {
            InitializeComponent();

            random = new Random();
            background_work = new Thread(Background_Work);

            running = true;
            background_work.IsBackground = true;
            background_work.Start();
        }

        private void Background_Work()
        {
            while (running)
            {
                myBar.Dispatcher.Invoke(() => { myBar.Value = random.Next(0, 100); });
                Thread.Sleep(1000);
            }
        }

        private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
        {
            running = false;
            background_work.Join();
        }
    }

Ответы [ 2 ]

0 голосов
/ 10 февраля 2019

Вызов background_work.Join() блокирует текущий поток, пока он ожидает естественного завершения потока background_work.Этот шаг протекает в цикле while, поэтому он никогда не заканчивается, и, следовательно, ваш пользовательский интерфейс ждет вечно.

Я бы посоветовал вам избегать потоков вместе и использовать Microsoft Reactive Framework (NuGet System.Reactive.Windows.Threading дляБиты WPF).

Тогда вы можете сделать это:

public partial class MainWindow : Window
{
    Random random;
    IDisposable subscription;

    public MainWindow()
    {
        InitializeComponent();

        random = new Random();

        subscription =
            Observable
                .Interval(TimeSpan.FromSeconds(1.0))
                .ObserveOnDispatcher()
                .Subscribe(_ => myBar.Value = random.Next(0, 100));
    }

    private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
    {
        subscription.Dispose();
    }
}

Это очень просто.

0 голосов
/ 08 февраля 2019

Я думаю, что происходит комбинация двух вещей, которые вместе вызывают тупик.

Во-первых, к полю running обращаются из нескольких потоков, но они не синхронизируются.Если вы обращаетесь к данным из нескольких потоков, они должны быть заблокированы .В этом случае, после того, как основной поток установит в поле значение false, фоновый поток может некоторое время считать его истинным.

Вторым является то, что вы вызываете Dispatcher.Invoke, а не Dispatcher.BeginInvoke.Invoke отправит сообщение в очередь сообщений диспетчера и будет ждать до тех пор, пока это сообщение не будет обработано, блокируя вызывающий поток.

Итак, что происходит:

  1. Поток диспетчера устанавливает running в false
  2. Блокирует поток диспетчера, ожидая выхода фонового потока
  3. Фоновый поток завершает свой сон, неправильно читает running as true, отправляет сообщение в очередь сообщений потока диспетчера и блокирует ожидание его обработки
  4. Сообщение никогда не будет обработано, поскольку поток диспетчера блокируется в ожидании выхода фонового потока
  5. Фоновый поток никогда не завершится, потому что он заблокирован в ожидании обработки сообщения потоком диспетчера
  6. Deadlock

Это происходит потому, что вы 'нарушая ряд различных кардинальных правил:

  1. Никогда не блокируйте поток пользовательского интерфейса.Всегда.
  2. Никогда не обращайтесь к общему состоянию из двух потоков без блокировки (или другой синхронизации).Когда-либо.
  3. Используйте Invoke поверх BeginInvoke только в том случае, если у вас есть действительно веская причина.

Как говорится в комментариях, используйте для этого DispatcherTimer.

Если вам действительно нужно отменить фоновую ветку, используйте CancellationToken.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...