重置System.Timers.Timer以防止Elapsed事件

时间:2013-02-13 19:05:01

标签: c# asp.net static timer

我正在尝试使用Timer来触发事件以通过网络发送数据。我创建了一个简单的类来进行调试。基本上我有List<string>我想发送。我想要发生以下事情:

  1. 将字符串添加到List
  2. 开始Timer 10秒
  3. List
  4. 之前将第二个字符串添加到Timer.Elapsed
  5. 10秒后重新启动Timer
  6. 到目前为止,我有这个:

    public static List<string> list;
    public static Timer timer;
    public static bool isWiredUp = false;
    
    public static void Log(string value) {
        if (list == null) list = new List<string>();
        list.Add(value);
    
        //this does not reset the timer, elapsed still happens 10s after #1
        if (timer != null) {
            timer = null;
        }
    
        timer = new Timer(10000);
        timer.Start();
        timer.Enabled = true;
        timer.AutoReset = false;
    
        if (!isWiredUp) {
            timer.Elapsed += new ElapsedEventHandler(SendToServer);
            isWiredUp = true;
        }
    }
    
    static void SendToServer(object sender, ElapsedEventArgs e) {
        timer.Enabled = false;
        timer.Stop();
    }
    

    有什么想法吗?

3 个答案:

答案 0 :(得分:11)

您可以使用Stop函数,紧接着Start函数来“重启”计时器。使用它可以在首次创建类时创建Timer,在此时连接Elapsed事件,然后在添加项目时调用这两种方法。它将启动或重新启动计时器。请注意,在尚未启动的计时器上调用Stop不执行任何操作,它不会引发异常或导致任何其他问题。

public class Foo
{
    public static List<string> list;
    public static Timer timer;
    static Foo()
    {
        list = new List<string>();
        timer = new Timer(10000);
        timer.Enabled = true;
        timer.AutoReset = false;
        timer.Elapsed += SendToServer;
    }

    public static void Log(string value)
    {
        list.Add(value);
        timer.Stop();
        timer.Start();
    }

    static void SendToServer(object sender, ElapsedEventArgs e)
    {
        //TODO send data to server

        //AutoReset is false, so neither of these are needed
        //timer.Enabled = false;
        //timer.Stop();
    }
}

请注意,您可能不希望使用List,而是使用BlockingCollection<string>。这有几个优点。首先,如果从多个线程同时调用,Log方法将起作用;因为多个并发日志可能会破坏列表。这也意味着SendToServer可以在添加新项目的同时将项目从队列中取出。如果您使用List,则需要lock对列表的所有访问权限(这可能不是问题,但不是那么简单)。

答案 1 :(得分:0)

使用IObservable(Rx)很容易实现这种功能。

让我们通过声明Subject<string>作为您的列表来使用.OnNext来简化问题。一旦你有了一个可观察的主题,你就可以通过单一的行来做你想做的事情。 System.Reactive.Linq。这在下面的伪c#

中说明
 subject
.Buffer(<your timespan>,1)   //buffer until either a value is added or the timeout expires
.Subscribe(x => 
   { 
      if (x.Count == 0)  //the timeout expired so send on
      {
         SendAccumulatedListToServer(<your list>);   
         <clear your list>
      }   
      else
      {
         <your list>.Add(x);
      }
   });

答案 2 :(得分:-4)

你正在实施的是完全错误的做法。看看消费者生产者模型:

http://msdn.microsoft.com/en-us/library/hh228601.aspx

您要做的事情通常被称为消费者/生产者数据流模型。基本上你有一些东西生成一个要在某处发送的数据列表,而不是每次将一个项目添加到你希望在组中发送它们的列表时发送它。所以你有一个生产者(代码将数据发送到被发送)和消费者(发送数据的代码)。

通常这个问题是通过生成一个监视列表的线程(通常是队列)并定期发送数据来解决的,最好的方法是使用EventWaitHandle。

以下是一些非常简化的代码示例

    class ServerStuff
{
    public void Init()
    {
        datatosend = new List<string>();
                    exitrequest = new EventWaitHandle(false, EventResetMode.ManualReset); //This wait handle will signal the consumer thread to exit
        Thread t = new Thread(new ThreadStart(_RunThread));
        t.Start(); // Start the consumer thread...
    }

    public void Stop()
    {
        exitrequest.Set();
    }

    List<string> datatosend;
    EventWaitHandle exitrequest;

    public void AddItem(string item)
    {
        lock (((ICollection)datatosend).SyncRoot)
            datatosend.Add(item);
    }

    private void RunThread()
    {
        while (exitrequest.WaitOne(10 * 1000)) //wait 10 seconds between sending data, or wake up immediatly to exit request
        {
            string[] tosend;
            lock (((ICollection)datatosend).SyncRoot)
            {
                tosend = datatosend.ToArray();
                datatosend.Clear();
            }

            //Send the data to Sever here...

        }
    }
}