#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
withCancellationToken
?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
, и все это должным образом закрывается. Спасибо всем за помощь!