【问题标题】:C# Making a Thread wait for a TimerC#让线程等待定时器
【发布时间】:2012-02-07 10:16:01
【问题描述】:

我正在编写一个同时运行两个 IRC 连接的 C# 程序。 连接是线程化的,每个线程都是这样开始的:

MainThread = new Thread(new ThreadStart(StartMainProcessor));
            MainThread.IsBackground = false;
            MainThread.Start();

private void StartMainProcessor() {
            MainProcessor.Bot.Connect();
            //while (true) { }
        }

Bot.Connect() 看起来像这样(有些删节):

public void Connect() {
            try {
                Client.Connect(IRCHelper.SERVER, IRCHelper.PORT);
            }
            catch (CouldNotConnectException e) {
                Reconnect(true);
                return;
            }

            try {
                Client.Listen();
            }
            catch (Exception e) {
                Reconnect(false);
                return;
            }
        }

在 bot 断开连接之前,这一切正常(最终总会发生,这是 IRC 的本质)。 当它断开连接时,会调用 Reconnect(),它会启动一个计时器。当该计时器到期时,机器人将再次调用 Connect()。计时器的原因是 IRC 服务器有时会拒绝立即重新连接。

然而,一旦 Connect() 方法结束,线程结束,程序(控制台应用程序)退出。 (Client.Listen() 正在阻塞)

我之前通过在 StartMainProcessor() 中添加 while (true) { } 来克服这个问题...但这会占用 100% 的 CPU,我真的更喜欢不同的解决方案。

感谢您的帮助。 :)

【问题讨论】:

  • 重新连接是否必须从与之前的连接相同的线程进行?

标签: c# .net multithreading timer


【解决方案1】:

听起来您需要一个信号结构。例如,您可以使用 AutoResetEvent 之类的东西来阻止调用 Reconnect 的线程,即调用 Reconnect,启动计时器,然后阻止线程。然后在计时器过期事件处理程序中设置自动重置事件,以允许线程继续(解除阻塞)并调用 Connect。

我不喜欢旋转处理器 - 添加无限循环或sleeps in loops 会浪费大量 CPU 资源。

【讨论】:

    【解决方案2】:

    你为什么不把Thread.Sleep 放在Bot.Reconnect 里面?这将使您的线程保持活动状态并在准备好再次调用Bot.Connect 时将其唤醒。

    【讨论】:

    • +1 睡眠/连接/监听循环是显而易见的解决方案。 OP 可能没有使用,因为所有帖子都说 sleep() 是一种反模式,必须使用计时器 :((
    • 我想这更有意义。我确实认为 Thread.Sleep() 不好。我想我应该少听网络,哈哈。 :) 谢谢,达伦。
    • @MartinJames, Thread.Sleep 一种反模式......它相对无害,但它仍然是一种反模式。
    【解决方案3】:

    您可能想尝试类似的方法

    private bool canExitThread;
    private void StartMainProcessor()
    {
        while (canExitThread)
        {
            //do the magic here
            System.Threading.Thread.Sleep(1); //make sure you allow thread to do the job, otherwise you will get 100 cpu usage
    
            //do the connecting, disconnecting, listening
        }
    }
    

    您还可以检查客户端是否已连接?如果是这样,那么您应该在主循环中检查它,如果它已断开连接 - 调用 connect 方法。

    希望能给你一个想法。

    还可以看看下面的文章,它可能会解释更多。 http://msdn.microsoft.com/en-us/library/aa645740(v=vs.71).aspx

    【讨论】:

    • 恶心!不要那样做...他可以阻止输入、信号量或告诉他何时退出的手动重置事件。
    【解决方案4】:

    这样的事情怎么样

    using System;
    using System.Diagnostics;
    using System.Threading;
    using System.Threading.Tasks;
    
    namespace Server
    {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Starting server..");
    
                foreach (var connection in new[] {new Connection(TimeSpan.FromSeconds(1)), new Connection(TimeSpan.FromSeconds(1))})
                    ThreadPool.QueueUserWorkItem(connection.Connect);
    
                Console.WriteLine("Server running. Press Enter to quit.");
    
                Console.ReadLine();
            }
        }
    
        public class Connection //might be good to implement IDisposable and disconnect on Dipose()
        {
            private readonly TimeSpan _reConnectionPause;
    
            public Connection(TimeSpan reConnectionPause)
            {
                _reConnectionPause = reConnectionPause;
            }
    
            //You probably need a Disconnect too
            public void Connect(object state)
            {
                try
                {
                    //for testing assume connection success Client.Connect(IRCHelper.SERVER, IRCHelper.PORT);
                    Debug.WriteLine("Open Connection");
                }
                catch (Exception)
                {
                    //You might want a retry limit here
                    Connect(state);
                }
    
                try
                {
                    //Client.Listen();
                    //Simulate sesison lifetime
                    Thread.Sleep(1000);
                    throw new Exception();
                }
                catch (Exception)
                {
                    Debug.WriteLine("Session end");
                    Thread.Sleep(_reConnectionPause);
                    Connect(state);
                }
            }
        }
    }
    

    【讨论】:

      【解决方案5】:

      我想你有一个Main 方法,所以我们为什么不从那里开始:

      private static readonly MAX_NUM_BOTS = 2;
      
      static void Main(string[] args)
      {
          List<Thread> ircBotThreads = new List<Thread>();
          for(int numBots = 0; numBots < MAX_NUM_BOTS; numButs++)
          {
              Thread t = new Thread(()=>{StartMainProcessor();});
              t.IsBackground = false;
              t.Start();
              ircBotThreads.Add(t);
          }
      
          // Block until all of your threads are done
          foreach(Thread t in ircBotThreads)
          {
              t.Join();
          }
      
          Console.WriteLine("Goodbye!");
      }
      
      private static void StartMainProcessor() 
      {
          MainProcessor.Bot.Connect();
      }
      

      然后你可以这样做:

      // 30 second time out (or whatever you want)
      private static readonly TimeSpan TIMEOUT = TimeSpan.FromSeconds(30.0);
      
      // specify the maximum number of connection attempts
      private static readonly int MAX_RECONNECTS = 10;
      
      public void Connect() 
      {
          bool shouldListen = false;
          // This is your connect and re-connect loop
          for(int i = 0; i < MAX_RECONNECTS; i++)
          {
              try 
              {
                  Client.Connect(IRCHelper.SERVER, IRCHelper.PORT);
                  shouldListen = true;
              }
              catch (CouldNotConnectException e) 
              {
                  // It's OK to sleep here, because you know exactly
                  // how long you need to wait before you try and
                  // reconnect
                  Thread.Sleep((long)TIMEOUT.TotalMilliseconds);
                  shouldListen = false;
              }
          }
      
          while(shouldListen)
          {
              try 
              {
                  Client.Listen();
              }
              catch (Exception e) 
              {
                  // Handle the exception
              }
          }
      }
      

      这是一个非常粗略的草稿,但概念是您不断尝试重新连接,直到失败。连接后,您就可以聆听(我假设您在 IRC 中聆听某些内容)并处理数据,直到您决定不再需要进行该工作。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-10-31
        • 2011-10-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多