Windows服务中的布尔标志不起作用

问题描述 投票:0回答:1

对不起我的英语不好,如果有人能帮我修理,我将不胜感激。

我编写了一个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;            
        }
    }
}
multithreading windows-services
1个回答
1
投票

您的代码很容易失败,因为竞争条件存在很大的“机会之窗”。

考虑这种潜在的情况:

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

您的处理执行两次。

问题是你检查的位置和设置布尔网守的位置之间的“窗口”太大了。检查和设置网守应该尽可能接近单个(原子)操作。检查和设置之间的窗口越大,竞争条件的可能性就越大。

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

© www.soinside.com 2019 - 2024. All rights reserved.