2013-02-13 15 views
5

Próbuję użyć Timer, aby uruchomić zdarzenie do wysyłania danych w sieci. Stworzyłem prostą klasę do debugowania. Zasadniczo mam List<string> Chciałbym wysłać. Chcę następujące wydarzy:Wyzeruj System.Timers.Timer, aby zapobiec Ellipemu zdarzeniu

  1. dodawania ciąg List
  2. Zacznij Timer przez 10 sekund
  3. Dodaj drugi ciąg List przed Timer.Elapsed
  4. Restart Timer z powrotem na 10 sekund.

Do tej pory mam to:

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

Jakieś pomysły?

Odpowiedz

11

Można użyć funkcji Stop następuje natychmiast przez funkcję Start do „restart” stoper. Używając tego, możesz utworzyć Timer, gdy klasa zostanie utworzona po raz pierwszy, a następnie połączyć się z wydarzeniem, które miało miejsce w tym czasie, a następnie wywołać tylko te dwie metody, gdy element zostanie dodany. Spowoduje to uruchomienie lub ponowne uruchomienie timera. Zauważ, że wywoływanie Stop na zegarze, który jeszcze się nie rozpoczął, po prostu nic nie robi, nie rzuca wyjątku ani nie powoduje żadnych innych problemów.

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

Zauważ, że zamiast używać List to bardzo możliwe, że chcesz użyć BlockingCollection<string> zamiast. Ma to kilka zalet. Po pierwsze, metody Log zadziałają, jeśli zostaną wywołane w tym samym czasie z wielu wątków; podobnie jak wiele równoczesnych dzienników może przerwać listę. Oznacza to również, że SendToServer może pobierać elementy z kolejki w tym samym czasie, gdy dodawane są nowe elementy. Jeśli używasz List, musisz mieć dostęp do całej listy (co może nie być problemem, ale nie jest tak proste).

+0

Z pytania Rozumiem, że chce on zgromadzić dane na liście i wysłać je tylko raz po 10 sekundach od ostatniego dodania do listy. Chce uniknąć wywołania SendToServera dwukrotnie, jeśli doda dane do listy dwa razy w odstępie trzech sekund. Mogę się mylić, ale tak to czytam. –

+0

@zespri Dokładnie to czytałem. Dokładnie to robi ten kod. – Servy

+0

nawet przy użyciu licznika czasu. Jest to zły pomysł. Nie ma blokady, żadnej formy synchronizacji. Czasomierz to absolutnie najgorszy sposób na zrobienie tego, powinien rozważyć, jak rozwiązuje ten problem. –

-4

To, co stosujesz, jest całkowicie niewłaściwym sposobem robienia tego. Zapraszamy do obejrzenia modelu producent konsumenckich:

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

Co próbujesz zrobić, to bardzo powszechnie nazywane model przepływu danych Consumer/producenta. Zasadniczo masz coś generującego listę danych, które mają zostać wysłane gdzieś, zamiast wysyłania ich za każdym razem, gdy element zostanie dodany do listy, którą chciałbyś wysłać w grupach. Więc masz producenta (kod wprowadzający dane do wysłane) i konsumenta (kod wysyłający dane).

Zasadniczo problem ten rozwiązuje się, odradzając wątek, który obserwuje listę (zwykle kolejkę) i wysyła dane w regularnych odstępach czasu, najlepszym sposobem na to jest użycie EventWaitHandle.

Oto bardzo uproszczony kod jako przykład

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

     } 
    } 
} 
+0

Twój link pokazuje "Ten temat jest już nieaktualny." i nie zawiera użytecznych informacji. –

+0

Nie mam pojęcia o czym mówisz lub co ta strona ma mi powiedzieć – tedski

+0

ok. Będę edytować i wyjaśniać więcej ... –

Powiązane problemy