Поток C # не умрет должным образом, пока я не пройду через код

#c# #multithreading

#c# #многопоточность

Вопрос:

Мой поток (_updater) запускается этим:

  public void StartCollection()
    {
        running = true;
        _updater = new Thread(new ThreadStart(ContinuousCollect));
        _updater.Start();
    }
  

Затем он собирает данные с датчиков и отправляет их туда, где это необходимо.

 private void ContinuousCollect()
    {
        while (running) //Static Boolean that controls whether or not to keep running.
        {
            lock (_data)
                ReadData(ref _data);

            //Stuff that doesn't matter
        }
    }
  

Затем он останавливается этим методом:

  public void StopCollection()
    {
        if (_updater != null)
        {
            if (_updater.ThreadState == ThreadState.Running)
            {
                running = false;
                _updater.Join();
            }

            _updater = null;
        }
    }
  

Когда я запускаю его в обычном режиме без точек останова, он выдает ошибку при попытке прочитать данные, но я уже закрыл COM-порт, чего он не должен был делать, поскольку я пытался завершить поток. Когда я использую точку останова в StopCollection() и выполняю пошаговое выполнение, он корректно изменяет running на false, а _updater останавливается, что не приводит к возникновению ошибок. Без точек останова _updater продолжает выполняться даже после того, как я вызываю StopCollection(), и для running остается установленным значение true, хотя единственное место в коде, где я установил для него значение true, находится в StartCollection(). Я попытался использовать Thread.Прервать, но это тоже не сработало. Что я могу сделать?

Комментарии:

1. Рассматривали ли вы возможность использования Task.Factory.StartNew with CancellationToken ?

2. Где вы закрываете порт?

3. В методе метод, который вызывает StopCollection(); после того, как он вызывает StopCollection(), он закрывает порт.

4. … и вы определенно идете по .Join пути, когда все работает не так, как ожидалось?

5. Если, если (_updater. ThreadState == Состояние потока. Запуск) не функционирует, да, я. Я запустил его с закомментированным _updater != null, но программа по-прежнему не работала.

Ответ №1:

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

Хотя вы можете каким-то образом явно синхронизировать доступ к переменной, чтобы гарантировать, что к ней правильно обращаются из нескольких потоков, более идиоматичным решением было бы использовать CancellationToken вместо этого, поскольку его реализация обеспечит надлежащую синхронизацию общего состояния, предотвращая кэширование состояния отмены токена в других потоках.

При отладке удаляются все виды оптимизаций, а также изменяются другие модели поведения, которые изменяют результаты неопределенного поведения, такого как это, вот почему ваша программа вела себя по-другому, когда вы использовали отладчик. Ошибки, связанные с потоками, очень часто трудно / невозможно воспроизвести с помощью отладчика по самой своей природе, поскольку ваше наблюдение за поведением изменяет само поведение, вот почему вам нужно быть намного осторожнее при программировании в многопоточной среде, использующей общую память.

Ответ №2:

Попробуйте сделать «запущенным» изменяемое поле. Также рассмотрите возможность использования Tasks и CancellationTokens, как предложил @Darek

Ответ №3:

Я «исправил» проблему, но, вероятно, вызвал другую в процессе. Когда он проверяет состояние потока, большую часть времени поток будет находиться в WaitSleepJoin состоянии, заставляя if (_updater.ThreadState == ThreadState.Running) отвечать false и, таким образом, никогда не отключать поток. Теперь он проверяет, находится ли он в Running или в WaitSleepJoin , и если это либо, это изменяет состояние Running , и все это должным образом закрывается. Спасибо всем за помощь!