Hallo!
Folgendes Verständnisproblem:
Grundlage:
IST-Zustand:
=> Problem: Aufgrund der hohen Emfangsdichte der Nachrichten werden zu viele Nachrichten nicht abgeholt und somit verloren
SOLL-Konzept:
Ein weiterer Thread für das Logging (Erzeuer/Verbraucher) soll die Daten speichern. Bringt das was? Oder welcher Ansatz wäre zu empfehlen? Kann ich das Logging optimieren?
Danke, mfg
Erstelle zwei Threads für das Abholen der Daten. Ein Thread nimmt nur die Daten entgegen und macht nichts anderes. Dieser Thread hat eine Liste, in der die Daten abgelegt sind.
Der zweite Thread nimmt sich immer vom ersten Thread die Daten, die ganz unten in der Liste sind. Nach der erfolgreichen Verarbeitung löscht Thread 2 die Daten aus der Liste von Thread 1.
So wäre sichergstellt, das alle Daten ankommen. Und Thread 2 ist nichts weiter als das "Arbeitstier" welches die Daten verarbeitet. Da es zwei getrennte Threads sind, ist es vollkommen egal wie lange Thread 2 für die Verarbeitung braucht. Thread 1 nimmt brav die Daten entgegen.
Das ganze muss natürlich gut synchronisiert sein.
"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)
Folgende Frage zur sauberen Synchronisation:
wollte eigentlich die Monitor Klasse in Verbindung mit locks verwenden. Natürlich wird jedoch die Wait, Pulse und PulseAll Methode nicht vom CF 2.0 unterstützt. Welche Alternativen habe ich da?
Ist das AutoResetEvent performant genug?
Danke, mfg
Da bin ich für das CF überfragt 🙂
Aber ich denke mal die Monitor Klasse brauchst du nicht. Würde an sich reichen, wenn du
private object m_Sync = new object();
// Später im Code
lock (this.m_Sync)
{
Thread1.Liste.Bla();
}
machst. So mach ich es jedenfalls bei solchen Szenarieren. Zwar unter dem großen Bruder vom CF, aber das dürfte egal sein.
Aber das kann dir für das CF garantiert hier jemand noch beantworten.
"Jedes Ding hat drei Seiten, eine positive, eine negative und eine komische." (Karl Valentin)
Sofern du nicht deine Leistung nicht sinnlos verheizt (busy waiting), dann ist deine CE-Plattform eben zu schwachbrüstig. Richtig teuer ist meiner Erfahrung nach die Anzeige. Dreh das mal komplett raus (oder nur jede x-te Botschaft) und schaue wo du stehst.
Hallo!
Danke für die Antworten!
Hab jetzt mal alles etwas umdesignt, dennoch ändert sich nix. Jenachdem wie ich die Thread Priorität verdrehe, entweder läuft mein Puffer voll, oder ich verliere nachrichten, oder im worstcase ist die gui nicht mehr ansprechbar. hab den code etwas optimiert, nicht wirklich merkbar.
noch eine Frage bzw. dem Warten, habe bisher immer ein Thread.Sleep(0), wenn z.B. der CAN Treiber keine Nachrichten im Puffer hat. Ist das so sinnvoll?
mfg
Thread.Sleep(0) bringt Dir nix - Dein Programm wird dort unnötig Ressourcen verbrauchen.
Wenn die Monitor-Klasse nicht vollständig unterstützt wird, dann ist es wahrscheinlich fraglich, ob Du überhaupt mit Threads arbeiten solltest, da der Overhead von Threads nur etwas bringt, wenn Du solche Techniken verfügbar sind. Ich denke, dass Threads nur dann Sinn machen, wenn Du eine klare Richtung hast und nur mit lock auskommst (z.B. für das Loggen, wo Du jede Sekunde mal schauen kannst, ob es etwas zu loggen gibt).
Mal ehrlich: Ohne Code können wir dir keine Tipps geben oder Fehler aufzeigen...
okay, so siehts aus (etwas vereinfacht), falls was wichtiges zum Verständnis fehlt, einfach Bescheid sagen.
//Klasse CANHandler
public void startCANRead()
{
//config thread
CANReadThreadIsAlive = true;
CANReadThread = new Thread(CANReadData);
CANReadThread.Priority = ThreadPriority.BelowNormal;
//message counter
count = 0;
CANReadThread.Start();
}
private void CANReadData()
{
messagesReceived = 0;
messagesLost = -1;
Int64 s1=0, s2=0;
//Stopwatch stopWatch = new Stopwatch();
while (CANReadThreadIsAlive)
{
CanPort.CanEvent ev;
Object newMessage = new Object();
try
{
/* Read events until error */
while (pCAN.ReadEventData(out ev) == 0)
{
//...
//Nachrichten auslesen & in Arraylist schaufeln
//...
//breaking reading
if (!CANReadThreadIsAlive)
{
CANReadThreadIsAlive = false;
count = 0;
frmMain.Instance.ThreadStopped();
break;
}
}
Thread.Sleep(0);
}
catch
{
//empty, jump into, if CE message queue is empty (normal just a few ms)
Thread.Sleep(0);
}
/* Wait for next event */
if (pCAN.WaitCommEvent(out mask) != 0)
throw new CanPortException("Error in WaitCommEvent()");
}
}
public void stopCANRead()
{
CANReadThreadIsAlive = false;
count = 0;
//loggingStep = 0;
frmMain.Instance.ThreadStopped();
}
//Klasse Logger
public void startLogging(string folder)
{
//...
//file anlegen usw.
try
{
//...
//feste daten in file schreiben, fehler vermeiden usw.
if (isLogging)
{
//config thread
MessageLoggerThreadIsAlive = true;
MessageLoggerThread = new Thread(saveMessagesThread);
MessageLoggerThread.Priority = ThreadPriority.BelowNormal;
MessageLoggerThread.Start();
}
}
catch (Exception e)
{
//Fehler abfangen usw.
}
}
public void saveMessagesThread()
{
while (MessageLoggerThreadIsAlive)
{
if (messageQueue.getNumber() == 0)
Thread.Sleep(0);
else
saveMessage(messageQueue.get());
}
}
public void saveMessage(Object o)
{
//...
//Daten aus Objekten lesen und in asc file schreiben per Streamwirter
sWriter.Flush();
}
//Klasse MessageQueue
public class MessageQueue
{
public static readonly MessageQueue Instance = new MessageQueue();
private ArrayList list = new ArrayList();
private static Object mutex = "";
private MessageQueue () {}
public Object get()
{
Object o;
lock (mutex)
{
if (list.Count > 0)
{
o = list[0];
list.RemoveAt(0);
return o;
}
return null;
}
}
public void insert(Object o)
{
lock (mutex)
{
list.Add(o);
}
}
public int getNumber()
{
return list.Count;
}
}
Hallo!
keine Antwort? Was für alternativen habe ich zum "Umschalten auf die anderen Threads"? Bin davon ausgegangen, dass ich mit Thread.Sleep(0) den aktuellen Thread die Ressourcen entziehe und ein anderer weiter arbeiten kann.?
mfg
Du wechselst schon mit Thread.Sleep(0) den Thread, aber die Frage ist, wann wieder der Kontext-Switch zurueck erfolgt - ich wuerde auf den Flush verzichten und den automatisch durchfuehren lassen, wenn der Buffer wirklich voll ist - sonst sehe ich nix, was ein Problem sein sollte.
Ich vermute, du hast ein GC-Problem. Wenn du viele Can-Botschaften bekommst, rennst du im CF locker alle 2-3 Sekunden in einen GC-Lauf. Ich würde - falls möglich - statt mit Objekten mit Strukturen arbeiten.
Wenn möglich solltest du mal mit den Performancecountern die Zahl der GC-Läufe ermitteln.
Danke für die Antworten. Aktuell hat sich einiges verbessert indem ich
den StreamWriter
Encoding enc = Encoding.ASCII;
sWriter = new StreamWriter(path, false, enc, 2200);
fest konfiguriert habe und zusätzlich das Flushen selbst übernehme. Jetzt verliere ich nur noch wenige Daten.
Die Puffergröße habe ich mir wie folgt erreichnet:
63-68 Zeichen pro Zeile ~ 70
alle 30 Zeilen lasse ich ihn schreiben + 100 Puffer.
Bemerke per Performancecounter schon Veränderungen um eine ganze Stelle. Jedoch nur alle 4-20 Durchläufe. Hätte gedacht, dass dies durchs Threading kommt. Wie kann ich direkt abfragen, ob der GC aktiv ist?
Notfalls bleibt mir noch die Möglichkeit, direkt im CANRead Thread Strings zu basteln und diese in den Puffer für den Logger zu schieben.
Danke, Gruß
Hallo all-finder oder auch jeder der mir helfen kann,
ist zwar schon etwas her, aber ich habe aktuell das gleiche problem.
Auf dem Windows CE Gerät landen Daten mittels CDLL (auch von mir) und IntPtr in meiner C# Anwendung, die eigentlich nur dazu gedacht ist die Daten über TCP IP an einen "normalen Windows Rechner" zu schicken (TCP IP in C würde ich mir gerne ersparen). Dort werden die Daten angezeigt, bearbeitet und wieder zurückgeschickt.
Das Windows CE Gerät arbeitet dann mit den bearbeiteten Daten weiter.
Dies geschieht in etwa alle 33ms. Kommt ein GC lauf dazwischen, sei es auf dem "normalen Windows Rechner" oder auf dem Windows CE Gerät gehen Daten verloren und das ist nicht akzeptable. Vielleicht hat ja jemand eine Idee, wo und wie ich diese gradlienige Verarbeitung aufbrechen kann, um ab und an etwas Zeit für den GC zu schaffen, ohne das Daten verloren gehen.
Gruß Luciluke