Логический флаг в Windows Service не работает - PullRequest
0 голосов
/ 10 декабря 2018

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

Я написал службу Windows, чтобы получать транзакции из базы данных и экспортировать их в обычный файл для отправки в другую систему через sftp,Эта услуга была запущена в течение 6 месяцев без каких-либо проблем.Мой код выглядит примерно так:

Я использовал логическую переменную с именем _isProcessOutwardMessage, чтобы проверить, запущен ли поток для запуска нового потока (processOutwardMessageThread).Было 5 флагов и 5 подобных тем (но я удалил 4, чтобы сохранить мой пост коротким)

Моя проблема в том, что примерно через 6 месяцев ИТ-специалисты что-то сделали на сервере (он сказал, что этозакалка).После этого обновления мой сервис столкнулся с ошибкой.Когда я проверил журнал, я обнаружил, что один и тот же поток (например: processOutwardMessageThread) был выполнен дважды в одно и то же время (похоже, флаг больше не работает).Это неверно, потому что он может быть запущен только после завершения предыдущего потока (флаг был установлен в false).

Я попытался перезапустить службу, но она работает нормально только около 1 часа, после этого снова возникает ошибка.Пожалуйста, дайте мне совет.Спасибо

using Timer = System.Timers.Timer;

namespace FastOne.Payment.MessageService
{
    internal partial class ProcessMessageService : ServiceBase
    {
        private Timer _processOutwardMessageTimer;

        public ProcessMessageService()
        {
            InitializeComponent();
        }

        private void LoadConfiguration()
        {
            try
            {                
                _processOutwardMessageInterval =
                    int.Parse(ConfigurationManager.AppSettings["ProcessOutwardMessageInterval"]);               

                //Running flag
                _isProcessOutwardMessage = false;
            }
            catch (Exception exception)
            {
                ProcessServiceLogger.Error(exception);
            }
        }

        protected override void OnStart(string[] args)
        {            
            //Load configs
            LoadConfiguration();

            _processOutwardMessageTimer = new Timer(_processOutwardMessageInterval);
            _processOutwardMessageTimer.Elapsed += ProcessOutwardMessageTimer_Elapsed;
            _processOutwardMessageTimer.Start();            
        }

        private void ProcessOutwardMessageTimer_Elapsed(object sender, ElapsedEventArgs e)
        {
            if (!_isProcessOutwardMessage)
            {
                var processOutwardMessageThread = new Thread(ProcessOutwardMessage);
                processOutwardMessageThread.Start();
            }
        }

        private void ProcessOutwardMessage()
        {
            try
            {
                ProcessOutwardMessageWithTransaction();
            }
            catch (Exception exception)
            {
                ProcessOutwardMessageLogger.Info("FAILED! ROLLBACK TRANSACTION!");
                ProcessOutwardMessageLogger.Error(exception);
            }
        }

        private void ProcessOutwardMessageWithTransaction()
        {
            //Set flag to true
            _isProcessOutwardMessage = true;

            //Do something here
            WriteLogToFile("Thread Execute.");

            //Set flag to false
            _isProcessOutwardMessage = false;            
        }
    }
}

1 Ответ

0 голосов
/ 07 января 2019

Ваш код подвержен сбоям, потому что существует большое «окно возможностей» для условий гонки.

Рассмотрим этот потенциальный сценарий:

  1. Таймер потока 1 истекает и ProcessOutwardMessageTimer_Elapsed () вызывается
  2. _isProcessOutwardMessage ложно, поэтому вызывается " var processOutwardMessageThread = new Thread (ProcessOutwardMessage) *
  3. ** Пока поток № 1 (и система) создают новый поток, выполнение которого может занять несколько миллисекунд, таймер потока № 2 отключается
  4. Поток № 2 видит, что _isProcessOutwardMessage имеет значение false, поэтому он вызывает " var processOutwardMessageThread = new Thread (ProcessOutwardMessage) ".
  5. Thread # 3 и Thread # 4 в конечном итоге создаются (соответственно Thread # 1 и Thread # 2) и обаcall ProcessOutwardMessageWithTransaction () .

Ваша обработка выполняется дважды.

Проблема в том, что«Окно» между тем, где вы проверяете и где вы устанавливаете логический привратник, слишком велико.Проверка и настройка привратника должны быть как можно ближе к одной (атомарной) операции, насколько это возможно.Чем больше промежуток между проверкой и установкой, тем больше вероятность возникновения условий гонки.

1033 * Объекты синхронизации были созданы для решения этой проблемы.Я предлагаю вам включить их в свое решение.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...