Windows Service中的布尔标志不起作用

时间:2018-12-10 15:33:04

标签: multithreading windows-services

对不起,我的英语不好,如果有人可以帮助我解决问题,我们将不胜感激。

我编写了Windows服务,用于从数据库获取事务并将其导出到平面文件,以通过sftp发送到另一个系统。该服务已运行6个月,没有任何问题。我的代码如下:

我使用了一个名为_isProcessOutwardMessage的布尔变量来检查线程是否正在运行以启动新线程(processOutwardMessageThread)。有5个标记和5个这样的线程(但为了使我的帖子简短,我删除了4个)

我的问题是:经过大约6个月的运行,IT人员在服务器中完成了一些工作(他说这已经很坚固了)。自此更新以来,我的服务遇到了错误。当我检查日志时,发现同一线程(例如: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 :(得分:1)

您的代码容易出错,因为在竞争条件下存在大量的“机会之窗”。

考虑这种潜在情况:

  1. 线程#1的计时器过去,并且 ProcessOutwardMessageTimer_Elapsed()被调用
  2. _isProcessOutwardMessage 为假,因此将调用“ var processOutwardMessageThread = new Thread(ProcessOutwardMessage)
  3. 线程1(和系统)正在创建新线程,这可能需要花费几毫秒才能完成,线程2的计时器会关闭
  4. 线程#2看到 _isProcessOutwardMessage 为假,因此它调用“ var processOutwardMessageThread = new Thread(ProcessOutwardMessage)”。
  5. 最终创建线程#3和线程#4(分别由线程#1和线程#2创建),并且都调用 ProcessOutwardMessageWithTransaction()

您的处理执行了两次。

问题是在您检查和设置布尔网守之间的“窗口”太大。检查和设置网守应尽可能接近单个(原子)操作。检查和设定之间的窗口越大,产生比赛状况的可能性越大。

Synchronization objects的创建是为了帮助解决该问题。我建议您将它们合并到您的解决方案中。