6

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

  1. 将字符串添加到List
  2. 开始Timer10 秒
  3. 将第二个字符串添加到List之前Timer.Elapsed
  4. 10 秒后重新启动Timer

到目前为止,我有这个:

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

有任何想法吗?

4

3 回答 3

12

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

于 2013-02-13T19:10:37.943 回答
0

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

让我们通过将 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);
      }
   });
于 2018-05-15T13:51:57.397 回答
-5

你正在实施的是完全错误的方式去做这件事。看看消费者生产者模型:

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

        }
    }
}
于 2013-02-13T19:09:23.257 回答