Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/302.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# Windows服务中的可重入计时器_C#_Multithreading_Windows Services_Timer_Interlocked - Fatal编程技术网

C# Windows服务中的可重入计时器

C# Windows服务中的可重入计时器,c#,multithreading,windows-services,timer,interlocked,C#,Multithreading,Windows Services,Timer,Interlocked,我想构建一个windows服务,它应该在不同的时间执行不同的方法。这与准确性无关。 Im使用system.timers.timer,并使用计数器调节Eventhandler方法中要执行的不同方法。到目前为止一切正常 所有方法都在访问COM端口,因此每次只需向一个方法授予访问权限。但是,由于这些方法可能需要一些时间才能完成,计时器可能会再次滴答作响,并希望在COM端口仍被占用时执行另一个方法。在这种情况下,事件可以也应该被驳回 简化为一种方法,我的elapsedEventHandler方法如下所示

我想构建一个windows服务,它应该在不同的时间执行不同的方法。这与准确性无关。 Im使用system.timers.timer,并使用计数器调节Eventhandler方法中要执行的不同方法。到目前为止一切正常

所有方法都在访问COM端口,因此每次只需向一个方法授予访问权限。但是,由于这些方法可能需要一些时间才能完成,计时器可能会再次滴答作响,并希望在COM端口仍被占用时执行另一个方法。在这种情况下,事件可以也应该被驳回

简化为一种方法,我的elapsedEventHandler方法如下所示(try-catch和这里排除的不同方法)

注意:虽然这在我的Win7 x64上运行得很好,但在安装了几乎相同软件的Win7 x86机器上,只要要执行的方法需要很长时间,它就会遇到困难。计时器不再滴答作响,不会引发异常。没有什么!我现在的问题是:我是否在做访问控制和计时器正确的部分,以便我可以专注于其他事情?我只是不太熟悉计时器,尤其是线程

     private static int m_synchPoint=0;
     private System.Timers.Timer timerForData = null;

    public MyNewService()
    {

        timerForData = new System.Timers.Timer();
        timerForData.Interval = 3000;
        timerForData.Elapsed += new ElapsedEventHandler(Timer_tick);
    }
    //Initialize all the timers, and start them
    protected override void OnStart(string[] args)
    {

        timerForData.AutoReset = true;
        timerForData.Enabled = true;
        timerForData.Start();
    }

    //Event-handled method
    private void Timer_tick(object sender, System.Timers.ElapsedEventArgs e)
    {
            ////safe to perform event - no other thread is running the event?                      
            if (System.Threading.Interlocked.CompareExchange(ref m_synchPoint, 1, 0) == 0)

            {
             //via different else-ifs basically always this is happening here, except switching aMethod,bMethod...
             processedevent++; 
             Thread workerThread = new Thread(aMethod);
             workerThread.Start();
             workerThread.Join(); 
             m_synchPoint=0;
             }
             else
             {
              //Just dismiss the event
              skippedevent++;
             }
     }   
提前非常感谢
非常感谢您的帮助

您可以尝试以下方法:

计时器启动时,禁用计时器


任务完成后,重新启用计时器…可能在Finally子句中。

我建议使用
System.Threading.timer
实现此功能。您可以在计时器执行时禁用它,处理数据,然后重新启用计时器

编辑:

我认为使用
System.Threading.Timer
更有意义,因为实际上没有理由将计时器放在设计图面上,这几乎是使用
System.Timers.Timer
的唯一理由。我真的希望微软能删除它,它是包装
System.Threading.Timer
,从一开始就不难使用

是的,您确实存在重新进入的风险,这就是为什么我指定将超时更改为
timeout.Infinite
。如果使用
Timeout.Infinite
构造计时器,则不会出现此重入问题

public class MyClass
{
    private System.Threading.Timer _MyTimer;

public MyClass()
{
    _MyTimer = new Timer(OnElapsed, null, 0, Timeout.Infinite);
}

public void OnElapsed(object state)
{
    _MyTimer.Change(Timeout.Infinite, Timeout.Infinite);
    Console.WriteLine("I'm working");
    _MyTimer.Change(1000, Timeout.Infinite);
}

}

如果要跳过方法调用,而上一个方法没有完成,只需在调用方法之前使用
Monitor.TryEnter(lockObject)

编辑: 这里有一个例子-

public class OneCallAtATimeClass
{

    private object syncObject;

    public TimerExample()
    {
      syncObject = new object();
    }

    public void CalledFromTimer()
    {    
      if (Monitor.TryEnter(syncObject);)
      {
        try
        {
          InternalImplementation();
        }
        finally
        {
          Monitor.Exit(syncObject);
        }
      }    
    }

    private void InternalImplementation()
    {
      //Do some logic here
    }

  }

在执行初始检查时,您可以正确地使用
CompareExchange
测试并设置
m_synchPoint
字段。错误地使用直接赋值将方法末尾的值重置为0。您应该改为使用将值重置为0。作为补充说明,您还应该将m_synchPoint更改为实例字段——它不应该是静态的。

我不知道发布的代码如何重新处理此问题。事实上,我无法在我的机器上重新处理此问题,只能在我正在测试的机器上,在Visual Studio中无法调试。这里的主要问题是:上面的代码是否正确使用了计时器并比较了交换,它的保存是假设一次只有一个方法在运行?Else:您将如何实现这样一个场景?这不是核心问题,但是不要对应该是实例特定的数据使用
静态
(在这种情况下,作为
m_synchPoint
),为什么要调用
workerThread.Start
,然后立即调用
workerThread.Join
?这样做没有任何好处,因为计时器线程只是等待新线程完成。直接执行
aMethod
,省去了创建新线程的开销。JimMischel:阅读我的log.debug,在我看来,情况并非如此,这也让我感到惊讶!这可能吗?我最初的想法是,明确建议服务等待完成@鲍比:当然对,但不是解决办法=(但是你也可以禁用
System.Timers.Timer
Timer.Enabled=false
。另外,我读到,即使在计时器停止后,也可以调用timerevents?这基本上就是我使用CompareeExchange@JimMischel:澄清我的答案的原因。@Bryan:我也不鼓励使用
System.Timers.Timer
,因为它会吞咽异常,隐藏bug。也就是说,包装之所以有用有两个原因:1)它使用事件模型而不是回调。通常.NET程序员更熟悉事件模型。其次,如果在Windows窗体应用程序中使用它,将
SynchronizingObject
设置到窗体会导致在GUI线程上调用事件处理程序,从而使您无需使用
invokererequired
调用
以访问控件。@蒂莫:是的,可能是计时器回调(或事件)可以在计时器停止后调用。但只有在前一个计时器的回调停止计时器之前发生下一个计时器滴答声时,才会发生这种情况。要发生这种情况,您需要一个非常快的计时器或非常慢的响应。这正是我的想法。您可以添加一个示例,显示在方法的开头调用
TryEnter
d、 正确的
try…终于
调用了
Monitor.Exit
。@JimMischel-当然,添加了一个示例ID说“有用”,但我不能xD