簡體   English   中英

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

[英]Reset System.Timers.Timer to prevent Elapsed event

我正在嘗試使用Timer來觸發事件以通過網絡發送數據。 我創建了一個簡單的類來進行調試。 基本上我有一個List<string>我想發送。 我希望發生以下情況:

  1. 將字符串添加到List
  2. 啟動Timer 10秒鍾
  3. Timer.Elapsed之前將第二個字符串添加到List
  4. 重啟Timer 10秒鍾。

到目前為止我有這個:

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

有任何想法嗎?

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

請注意,您可能希望使用BlockingCollection<string> ,而不是使用List 這有幾個優點。 首先,如果從多個線程同時調用,則Log方法將起作用; 因為多個並發日志可能會破壞列表。 這也意味着SendToServer可以在添加新項目的同時將項目從隊列中取出。 如果您使用List ,則需要lock對列表的所有訪問權限(這可能不是問題,但不是那么簡單)。

使用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);
      }
   });

你正在實施的是完全錯誤的做法。 看看消費者生產者模型:

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

您嘗試做的通常稱為Consumer / Producer數據流模型。 基本上你有一些東西生成一個要在某處發送的數據列表,而不是每次將一個項目添加到你希望在組中發送它們的列表時發送它。所以你有一個生產者(代碼將數據發送到被發送)和消費者(發送數據的代碼)。

通常,通過生成監視列表(通常是隊列)並以定期間隔發送數據的線程來解決此問題,最好的方法是使用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...

        }
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM