Reputation: 2311
I am trying to use the Timer
to 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:
List
Timer
for 10 secondsList
before Timer.Elapsed
Timer
back at 10 seconds.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?
Upvotes: 6
Views: 25515
Reputation: 3697
This kind of thing is very easy to achieve with 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#
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);
}
});
Upvotes: 0
Reputation: 1099
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
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.
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...
}
}
}
Upvotes: -5
Reputation: 203847
You can use the Stop
function followed immediately by the Start
function to "restart" the timer. Using that you can create the Timer
when 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 Stop
on a timer that hasn't yet been started just does nothing, it doesn't throw an exception or cause any other problems.
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 List
it's very possible that you want to use a BlockingCollection<string>
instead. This has several advantages. First, the Log
methods will work if called at the same time from multiple threads; as is multiple concurrent logs could break the list. It also means that SendToServer
can be taking items out of the queue at the same time that new items are added. If you use a List
you'll need to lock
all access to the list (which might not be a problem, but isn't as straightforward).
Upvotes: 12