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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/14861047/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-10 13:15:18  来源:igfitidea点击:

Reset System.Timers.Timer to prevent Elapsed event

c#asp.netstatictimer

提问by tedski

I am trying to use the Timerto trigger an event to send data across the network. I created a simple class to debug. Basically I have a List<string>I'd like to send. I want the following to happen:

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

  1. Add string to List
  2. Start Timerfor 10 seconds
  3. Add second string to Listbefore Timer.Elapsed
  4. Restart Timerback at 10 seconds.
  1. 将字符串添加到 List
  2. 开始Timer10 秒
  3. 将第二个字符串添加到List之前Timer.Elapsed
  4. Timer10 秒后重新启动。

So far I have this:

到目前为止,我有这个:

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();
}

Any ideas?

有任何想法吗?

采纳答案by Servy

You can use the Stopfunction followed immediately by the Startfunction to "restart" the timer. Using that you can create the Timerwhen the class is first created, wire up the Elapsed event at that time, and then do nothing but call those two methods when an item is added. It will either start, or restart, the timer. Note that calling Stopon a timer that hasn't yet been started just does nothing, it doesn't throw an exception or cause any other problems.

您可以使用该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();
    }
}

Note that rather than using a Listit's very possible that you want to use a BlockingCollection<string>instead. This has several advantages. First, the Logmethods will work if called at the same time from multiple threads; as is multiple concurrent logs could break the list. It also means that SendToServercan be taking items out of the queue at the same time that new items are added. If you use a Listyou'll need to lockall access to the list (which might not be a problem, but isn't as straightforward).

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

回答by Matthew Sanford

What you are implementing is totally the wrong way to go about doing this. Have a look at the consumer producer model:

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

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

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

What you are trying to do is very commonly called the Consumer/Producer dataflow model. Essentially you have something generating a list of data that is to be sent somewhere, rather than sending it each time an item is added to the list you would like to send them in groups.. So you have a producer (the code putting data to be sent) and a consumer (the code sending the data).

您尝试执行的操作通常称为消费者/生产者数据流模型。本质上,您有一些东西会生成要发送到某处的数据列表,而不是每次将项目添加到列表中时发送它,您希望将它们分组发送.. 所以你有一个生产者(将数据放入的代码)被发送)和消费者(发送数据的代码)。

Generally this problem is solved by spawning a thread that watches the list (usually a queue) and sends the data at regulary intervals, the best way to do this is using an EventWaitHandle.

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

Here is some very simplified code as an example

下面是一些非常简化的代码作为示例

    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...

        }
    }
}

回答by Sentinel

This kind of thing is very easy to achieve with IObservable (Rx).

这种事情用IObservable(Rx)很容易实现。

Let us simplify matters by declaring a Subject<string>as your list to push onto using .OnNext. Once you have your subject, an observable, you can do what you want with a single 'line' of System.Reactive.Linq. This is illustrated in the following pseudo-c#

让我们通过将 a 声明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);
      }
   });