秒表在单独的线程中重新启动

StopWatch in separate thread restart

我有一个客户端-服务器应用程序,我想定期检查客户端是否已与服务器断开连接。

我决定检查传入的数据包。如果我收到任何时间跨度,比如 15 秒,我有一个有效的连接, 如果不是,我已断开连接并将尝试重新连接。

到目前为止我有这个示例代码(这是根据我的代码重新创建的示例):

namespace TimerExample
{
    class Program
    {
        static void Main(string[] args)
        {
           HandlePackets();
        }

        public void HandlePackets()
        {
            //code that handles incomming packets
            foo test = new foo();

            test.StartThread();
        }
    }

    class foo
    {
        public bool _isRunning { get; set; }
        private Stopwatch sw { get; set; }

        public void StartThread()
        {
            this._isRunning = true;
            new Thread(new ThreadStart(this.DoWork)).Start();

            this.sw.Restart();
        }

        public void StopThread()
        {
            this._isRunning = false;
            this.sw.Stop();
        }

        private void DoWork()
        {
            while (this._isRunning)
            {
                Console.WriteLine("Elapsed in miliseconds: " + this.GetRuntime().ToString());

                if (GetRuntime() > 15000)
                {
                    Console.WriteLine("Client disconnected.... restarting");
                    this.StopThread();
                }

                Thread.Sleep(1000);
            }
        }

        public long GetRuntime()
        {
            return this.sw.ElapsedMilliseconds;
        }

        public foo()
        {
            _isRunning = false;
            sw = new Stopwatch();
        }
    }
}

我想让代码做的是:函数 HandlePackets 将在每次数据包到达时执行。在那个函数里面我会调用 函数 StartThread 将 运行 Stopwatch 在单独的线程中,只要经过 stopwatch 以毫秒为单位的时间,此过程就会继续 不会大于 15 秒。

如果可以,我会打电话给 Reconnect

所以基本上每次收到数据包时计时器都会重新启动,如果 ElapsedTime 大于 15 秒,则会调用重新连接。

有几种方法可以实现这个机制。

创建线程是最糟糕的一个。 小心 - 从多个线程访问 Stopwatch 实例成员是不安全的。

一个简单直接的解决方案是创建 ThreadPool Timer,假设每 15 秒滴答一次,并通过 Volatile.Read 检查布尔变量。一旦布尔变量为 False - 您可以重新连接。 从接收器线程,您只需要使用 Volatile.Write true 设置变量。这在接收时不消耗资源(几乎)。

在许多实现中,由于重新连接机制可以在新数据包到达之前的片刻开始,因此可能会出现竞争。改进这一点的最简单和流氓方法是在您决定重新连接之前停止计时器,并在连接完成后再次启动它。您必须明白,没有办法解决这个假重连问题。

上述方法与 WatchDog 非常相似

从设计的角度来看,我建议您创建 类:Receiver 和 WatchDog 以及 ConnectionManager

// Receives and processes data
class Receiver : IDisposable
{
  public Receiver(WatchDog watchDog);
  public void LoopReceive(); // Tick watch dog on every packet
  public void Dispose();
}

// Setups timer and periodically checks if receiver is alive.
// If its not, it asks manager to reconnect and disposes receiver
class WatchDog : IDisposable
{
  public WatchDog(ConnectionFactory factory);
  // Setups timer, performs Volatile.Read and if receiver is dead, call dispose on it and ask manager to reconnect.
  public void StartWatching(IDisposable subject);
  public void Tick(); // Volatile.Write
  public void Dispose();
}

// Can re-connect and create new instances of timer and watchdog
// Holds instance variable of receiver created
class ConnectionManager
{
  public void Connect();
  // disposes watch dog and calls connect
  public void ReConnect(WatchDog watchDog);
}

PS: Volatile.* 可以用标志变量的 volatile 关键字替换