Laden...

Forenbeiträge von #CPferdchen Ingesamt 28 Beiträge

24.11.2015 - 14:43 Uhr

Einsteiger sind für diese Art von Codestyle die falsche Zielgruppe.

Dann wie von dir schon vorgeschlagen:


Console.WriteLine(string.Format("Der Gesamtbetrag nach dem {0}. Jahr beträgt {1:N2} Euro.",i,jahreumsatz);

Ich würde mich selbst auch definitiv als Einsteiger bezeichnen.

24.11.2015 - 12:33 Uhr

 Console.WriteLine($"Der Gesamtbetrag nach dem {i}. Jahr beträgt {jahreumsatz:N2} Euro.");
20.11.2015 - 11:59 Uhr

Und um die Frage noch beantworten:
doch, es ist schlimm, denn das ist einfach das falsche Anwenden von async/await 😃

Im Sinne von: Es ist falsch in einen geladenen Lauf zu gucken, aber kann man machen, wenn man Aufpasst, dass man nicht den Abzug drückt? Aber trotzdem nicht machen, weil man nicht weiss ob nicht ein andere am Abzug rum fummelt?

Habs jetzt mit PerformanceCounter gelösst. Sehe ich fürs erste als hinreichend in Abhängigkeit von Aufwand und ich bekomme noch Messungen des Arbeitsspeichers und der CPU dabei . Static fliegt später raus und die Daten landen in einer Datei die über eine extra App ausgewertet werden können und das Messen soll später in der Hauptapp zu und abschaltbar sein.


//gekürzter Auszug
public static class Statistics
  {
    public static PerformanceCounter CpuPerformanceCounter;
    public static PerformanceCounter RamPerformanceCounter;
    public static PerformanceCounter TaskPerformanceCounter;

    static Statistics ()
    {
      CpuPerformanceCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
      RamPerformanceCounter = new PerformanceCounter("Memory", "Available MBytes");
      TaskPerformanceCounter = CreateCustomCounters("Task-Counter", true);
    }

private static PerformanceCounter CreateCustomCounters(string category, bool deleteIfExists)
    {
      PerformanceCounter performanceCounter = new PerformanceCounter();

      if (deleteIfExists && PerformanceCounterCategory.Exists(category))
      {
        PerformanceCounterCategory.Delete(category);
      }

      if (!PerformanceCounterCategory.Exists(category))
      {
        CounterCreationDataCollection counterCollection = new CounterCreationDataCollection();

        CounterCreationData opsPerSec = new CounterCreationData();
        opsPerSec.CounterName = "# requests /sec";
        opsPerSec.CounterHelp = "Number of requests executed per second";
        opsPerSec.CounterType = PerformanceCounterType.RateOfCountsPerSecond32;
        counterCollection.Add(opsPerSec);

        CounterCreationData operationTotal = new CounterCreationData();
        operationTotal.CounterName = "Total # requests";
        operationTotal.CounterHelp = "Total number of requests executed";
        operationTotal.CounterType = PerformanceCounterType.NumberOfItems32;
        counterCollection.Add(operationTotal);

        PerformanceCounterCategory.Create(
          category,
          "A custom counter category that tracks the number of Tasks running",
          PerformanceCounterCategoryType.SingleInstance,
          counterCollection);
      }

      performanceCounter = new PerformanceCounter(category, "Total # requests", false);
      performanceCounter.ReadOnly = false;
      performanceCounter.RawValue = 0;

      return performanceCounter;
    }
}

Und ClientTask mit normal und async, weil ich noch nich sicher bin was ich später brauchen/verwenden werde / sinnvoll ist:


internal void NewChannelData(List<DispatchChannel> channels)
    {
      NewChannelDataAsync(channels).Wait();
    }

    internal async Task NewChannelDataAsync(List<DispatchChannel> channels)
    {
      Statistics.TaskPerformanceCounter.Increment();
      await Task.Run(
      () =>
      {
        try
        {
          lock (_lockObject)
          {
            channels.ForEach(channel => ChannelMapping(channel));
          }
        }
        catch (Exception ex)
        {
          //TODO
        }
        finally
        {
         Statistics.TaskPerformanceCounter.Decrement();
        }
      });
    }

Vielen Dank für die Ratschläge 😃

19.11.2015 - 16:04 Uhr

Hab das ganze jetzt mal stark umgebaut und die Queue raus geworfen.

Das ist aber hier nicht die Frage, sondern wie Du den Zugriff auf Deinen Counter synchronisierst - sprich, wie AddTaskCounter und SubstractTaskCounter implementiert sind.

Gar nicht. Methoden sind aber auch sowiso raus geflogen und zähle jetzt TaskCounter direkt rauf und runter.


 internal async Task NewChannelDataAsync(List<DispatchChannel> channels)
    {
      await Task.Run(
      () =>
      {
        try
        {
          lock (_lockObject)
          {
            Statistics.TaskCounter++;
            channels.ForEach(channel => ChannelMapping(channel));
          }
        }
        catch (Exception ex)
        {
        }
        finally
        {
          lock(_lockObject)
          {
            Statistics.TaskCounter--;
          }
        }
      });
    }

Schau Dir mal die Monitoring-Namespace von .NET an.

Kannst du mir eine empfehlen? Scheint ja eine ganze Menge zu geben die sehr viel können. Aber für meine Zwecke und Fertigkeiten dann etwas zu unhandlich.

Wie wäre es den mit dem System.Diagnostics.PerformanceCounter mit Increment() und Decrement()?

Any public static members of this type are thread safe.

Bekomme den Burschen nur grad nicht zum laufen 😦


public static PerformanceCounter PerformanceCounter;
    static Statistics ()
    {
      _dataHistoryVector = new Vector (20);
      PerformanceCounter = new PerformanceCounter();
      PerformanceCounter.CategoryName = "TaskCounter";
      PerformanceCounter.CounterName = "PerformanceCounter";
      PerformanceCounter.InstanceName = "Client Task";
    }

Aber leider bekomme ich hier eine InvalidOpEx:

labelTaskCounter.Text = Statistics.PerformanceCounter.NextValue().ToString();

{"Die Kategorie ist nicht vorhanden."}

Muss der String in CategoryName ein bestimmter String sein?

EDIT:
Oder direkt Interlock auf dem der PerformanceCounter wohl basiert?

EDIT2:
Die Methode NewChannelData ist nur Async falls ich das mal brauche? Ist ja nicht schlimm außer, dass VS meckert, oder?

19.11.2015 - 13:40 Uhr

Ich bau eine Applikation die verschiedene Geräte über Channel verwaltet und als serialisierte Listen von einer Basisklassen von Channel(Wert und Addresse) an Clients über WCF (TCP) sendet. Ich will hier eigentich nur beobachten, dass es zu keiner "Anhäufung" von Task kommt. Also die Daten schnell genug in die Queue geschoben werden können. Also der Task der die Daten wieder raus holt und verarbeitet die Queue nicht so häufig speert, dass die Daten nicht schnell genug verarbeitet werden können, weil sie die meiste Zeit in der Queue abhängen würden und ich somit die Anforderung an die Frequenz mit denen ich Daten empfange nicht erfülle.

19.11.2015 - 12:09 Uhr

Was benutzt Du zur Synchronisation? lock, Interlocked oder etwas anderes?
Deine Beschreibung legt nahe, daß Du das entweder gar nicht oder falsch machst.

Ich benutze eine synchronized Queue:


private readonly Queue channelQueue = Queue.Synchronized (new Queue ());

Queue.Synchronized Method

The wrapper returned by this method locks the queue before an operation is performed so that it is performed in a thread-safe manner.

Davon abgesehen, dass statische Klassen i.d.R. ein Hinweis auf ein falsche /suboptimales Design sind... was genau willst Du bei der Statistik denn abdecken?
Ist es Logging, Auditing, Performance-Messerungen, Queue-Überwachungen?

Für eigentlich alles gibt es schon externe Elemente, die sich einfach "draufpacken" lassen statt den eigenen Code beeinflussen.
Der Quellcode sollte nie so gestaltet sein, dass externe Aspekte wie Deployment oder Tracking den Code negativ in seiner Ausführung oder Aufbau beeinflussen.

Ich stecke grad noch in der Entwurfsphase und plane nicht die Applikation inklusive dieser statischen Klasse zu veröffentlichen. Für mich nur ein simpler Weg um schnell meine Performance grob bewerten zu können. (Ohne mich lange in irgendwas ein arbeiten zu müssen. Was allerdings langfristig sinnvoll sein wird.)

19.11.2015 - 11:27 Uhr

Hallo,

Ich habe eine statische Klasse Statistics in der ich zu Testzwecken einige Werte die in meiner App anfallen ablege um verschiedene Vorgehensweisen hinsichtlich der Performance bewerten zu können. Dazu zählt eine Propertie TaskCounter mit der ich die Anzahl laufender Instanzen einer bestimmten Task überwachen will. Diese zähle ich mit der statischen Methode AddTaskCounter() hoch und mit SubstractTaskCounter() runter.


    internal void NewChannelData (List<DispatchChannel> channels)
    {
      //Methode wird aus Servertask herraus angestoßen, wenn Channel zu versenden sind. Bekommt _shouldBeDispatched übergeben
      //und fügt die einzelnen Channel daraus einer Synchronized Queue an, welche wiederrum von Clienttask abgearbeitet wird
      Statistics.AddTaskCounter ();
      new Task (
        () =>
        {
          channels.ForEach (channel => channelQueue.Enqueue (channel));
          //Variante B:
          //Statistics.SubstractTaskCounter();
        }).Start ();
      //Variante A
      Statistics.SubstractTaskCounter();
    }

Im Code seht ihr, dass ich zwei Varianten für die Position der Substract Anweisung markiert habe. Mein Problem ist nun, dass bei Variante B es manchmal vorkommt, dass TaskCounter negativ wird. Wie kann das sein? Den das hieße ja, dass SubstractTaskCounter() häufiger ausgeführt wurde als AddTaskCounter().
Den Wert der Propertie ändere ich sonst nirgens in der Applikation. Noch rufe ich die beiden Methode woanders auf als in NewChannelData.

Mit Variante A scheint alles ok, aber irgendwie habe ich nun kein Vertrauen mehr in meine Statistik.

EDIT:
Variante A ist auch kacke, weil der Zähler runter gezählt wird, auch dann wenn die Task noch nicht beendet ist. Oder?

07.08.2015 - 13:45 Uhr
await tuble.Item1.WriteLineAsync(serializedPoints);
         await tuble.Item1.FlushAsync ();

😜

Scheinbar wird sonst gewartet bis der Buffer voll

07.08.2015 - 11:34 Uhr

Hallo,

Ich habe mir einen TCP Server gebastelt, der Listen von Points mit JSON serialisiert und über eine TCP Verbindung an einen Client zyklisch sendet. Imo 1 Sekunde über einen Timer.

Der Server:


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace WindowsFormsTcpTest
{
  public class TcpIpServer
  {
    private readonly List<Tuple<StreamWriter, TcpClient>> _streamWriters = new List<Tuple<StreamWriter, TcpClient>> ();
    private readonly Stopwatch _stopwatch;
    private TcpListener _tcpListener;

    public TcpIpServer (int port)
    {
      StartAcceptingClients (port);

      _stopwatch = new Stopwatch ();
      _stopwatch.Start ();
    }

    private void StartAcceptingClients (int port)
    {
      _tcpListener = new TcpListener (IPAddress.Any, port);
      _tcpListener.Start ();

      new Task (
        () =>
        {
          while (true)
          {
            if (_tcpListener.Pending () == false)
            {
              Thread.Sleep (100);
              continue;
            }

            TcpClient tcpClient = _tcpListener.AcceptTcpClient ();
            tcpClient.NoDelay = true;

            if (tcpClient.Connected)
            {
              _streamWriters.Add (Tuple.Create (new StreamWriter (tcpClient.GetStream ()), tcpClient));
            }
          }
        }).Start ();
    }

    private int i = 0;

    public async void TransmitPoints (List<Point> points)
    {
      points[0] = new Point(i++,0);

      string serializedChannels = JsonConvert.SerializeObject (points);
      Console.WriteLine ("{0} | {1}", serializedChannels, _stopwatch.ElapsedMilliseconds);

      foreach (var tuble in _streamWriters)
      {
        if (tuble.Item2 != null || tuble.Item2.Connected)
        {
          await tuble.Item1.WriteLineAsync(serializedChannels);
        }
        else
        {
          tuble.Item1.Dispose ();
          _streamWriters.Remove (tuble);
        }
      }
    }
  }
}

Und dessen Verwendung:

using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace WindowsFormsTcpTest
{
  public partial class Form1 : Form
  {
    private readonly TcpIpServer _tcpIpServer;
    private readonly List<Point> _points;

    public Form1 ()
    {
      InitializeComponent ();
      _tcpIpServer = new TcpIpServer (4711);
      _points = new List<Point> ();
      _points.Add (new Point (0, 0));
      _points.Add (new Point (0, 1));
    }

    private void Timer1Tick (object sender, System.EventArgs e)
    {
      _tcpIpServer.TransmitPoints (_points);
    }
  }
}

Nun habe ich ein Problem mit diesem Client:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Net.Sockets;
using System.Threading.Tasks;
using Newtonsoft.Json;

namespace WindowsFormsTcpClientTest
{
  public class TcpIpClient
  {
    private readonly TcpClient _tcpClient;
    private readonly Action<List<Point>, long> _action;

    public TcpIpClient (string ip, int port, Action<List<Point>, long> action)
    {
      _tcpClient = new TcpClient ();
      _tcpClient.Connect (ip, port);
      _action = action;
    }

    public void StartReceivingPoints ()
    {
      if (_tcpClient.Connected == false || _tcpClient == null || _action == null)
      {
        return;
      }

      StreamReader streamReader = new StreamReader (_tcpClient.GetStream ());

      Stopwatch watch = new Stopwatch ();
      watch.Start ();

      new Task (async () =>
      //new Task(() =>
        {
          while (_tcpClient.Connected)
          {
            string read = await streamReader.ReadLineAsync();
            //string read = streamReader.ReadLine();
            List<Point> channels = JsonConvert.DeserializeObject<List<Point>> (read);

            if (_action != null)
            {
              _action (channels, watch.ElapsedMilliseconds);
            }
          }
        }).Start ();
    }
  }
}
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Windows.Forms;

namespace WindowsFormsTcpClientTest
{
  public partial class Form1 : Form
  {
    public Form1 ()
    {
      InitializeComponent ();

      TcpIpClient tcpIpClient = new TcpIpClient ("192.168.5.185", 4711, printPoints);
      tcpIpClient.StartReceivingPoints ();
    }

    public void printPoints (List<Point> points, long milliseconds)
    {
      string s = points.Aggregate ("", (current, point) => current + (point + " | "));
      Console.WriteLine ("{0} > {1}", s, milliseconds);
    }
  }
}

Eigentlich würde ich ja erwarten, dass printPoint() in etwa mit dem selben Takt die Ausgaben weiter gibt wie die Listen gesendet werden. Aber ich statt dessen wird der Buffer scheinbar paketweise ausgelesen (Siehe X als Counter und rechts die EllapsedMilliseconds von Stopwatch):
{X=8,Y=0} | {X=0,Y=1} | > 58332
{X=9,Y=0} | {X=0,Y=1} | > 58339
...
{X=63,Y=0} | {X=0,Y=1} | > 58359
{X=64,Y=0} | {X=0,Y=1} | > 58359
{X=65,Y=0} | {X=0,Y=1} | > 113934
{X=66,Y=0} | {X=0,Y=1} | > 113934
...
{X=118,Y=0} | {X=0,Y=1} | > 113967
{X=119,Y=0} | {X=0,Y=1} | > 113967
{X=120,Y=0} | {X=0,Y=1} | > 168720
{X=121,Y=0} | {X=0,Y=1} | > 168721

Eigentlich hatte ich StreamReader und StreamWriter genutzt um mit Read/WriteLine sicher zu gehen, dass der komplette Datensatz gesendet wird ohne, dass ich selber dafür sorgen zu müssen gestückelte Strings wieder korrekt zusammen zu setzen. Ob ich nun ReadLine oder ReadLineAsyc verwende macht auch keinen Unterschied.

Irgendwie erkenne ich nicht wo das Problem liegt 😦 Könnt ihr mir weiter helfen.

Danke

31.07.2015 - 09:54 Uhr

Hallo,

Vielen Dank für eure Antworten.

wenn du eh am Server arbeitest, so kannst du dort eine richtige Datenbank verwenden. Z.B. SQL Server und dort die Daten hinschreiben.

Würde aber heißen, dass immer wenn ich ein neues System mit dem Geräteserver aufsetze ich auch einen SQL Server mit aufsetzen muss. Ein zentraler SQL Server in unserem Unternehmen wäre hier die Lösung, aber der Geräteserver soll auch isoliert vom Netz funktionieren.

Alternativ kannst du auch einen Logging-Dienst erstellen und dein Programm kommuniziert mit diesem Dienst

Das ganze als Dienst laufen zu lassen ist eine super Idee. Kannte ich als eigene Anwedung in C# bis her nicht. Wenn ich mir dieses Tutotial Windows Services mit C#so anschaue wäre das vielleicht auch was für meine TCP Client/Server Kommunikation.

Ob ein Task reicht od. nicht hängt von der Anwendung ab.

Wie meinst du das? Die Anzahl der Task oder ob ich einen Dienst verwende?

Ich würde Dir an Deiner Stelle Event Tracing empfehlen.
Es ist etwas komplexer als normales Logging, dafür rasend schnell - so wird es in der CLR für die Garbage Collector Statistiken benutzt.
Ich habe selber etwas dazu auf meinem Blog vor kurzem geschrieben.

Rasend schnell hört sich gut an 😄. Werde mal versuchen dazu eine Test Applikation zu schreiben. Hast du noch weitere nützliche Links zu dem Thema?

Ich habe gestern dieses mit SQLite gebastelt, aber euere Lösungen hören sich schneller und professioneller an.


using System;
using System.Collections;
using System.Collections.Generic;
using System.Data.SQLite;
using System.Threading;
using System.Threading.Tasks;
using VB.Core.Channel;


//TODO: Erkennen und separieren der Channel auf einzelne Tables
//TODO: Eigene Spalten in den Tables statt einen String
//TODO: gescheiten Zeitstempel anlegen

namespace VB.Server
{
  public class Data
  {
    public Data (ChannelAddress channelAddress, Type channelType, object value)
    {
      ChannelAddress = channelAddress;
      ChannelType = channelType;
      Value = value;
    }

    public ChannelAddress ChannelAddress { get; set; }
    public Type ChannelType { get; set; }
    public object Value { get; set; }

    public override string ToString ()
    {
      return string.Format ("{0} | {1} | {2}", ChannelAddress, ChannelType, Value);
    }
  }

  public static class DataHistory
  {
    public static Queue SynchronizedQueue = Queue.Synchronized (new Queue (new Queue<Data> ()));
    private static readonly object _lockObject = new object ();

    public static void syncEnqueue (ChannelAddress channelAddress, Type channelType, object value)
    {
      lock (_lockObject)
      {
        SynchronizedQueue.Enqueue (new Data (channelAddress, channelType, value));
      }
    }

    public static string syncDequeue ()
    {
      lock (_lockObject)
      {
        return  (SynchronizedQueue.Dequeue ()).ToString ();
      }
    }
  }

  public class DataHistoryTask
  {
    private bool _shouldStop;
    private const int TransactionLimit = 100;

    public void Start ()
    {
      new Task (
        () =>
        {
          
          SQLiteConnection.CreateFile ("sqliteTest.db");
          SQLiteConnection sqLiteConnection = new SQLiteConnection ();
          sqLiteConnection.ConnectionString = "Data Source = sqliteTest.db; Version=3";
          sqLiteConnection.Open ();

          SQLiteCommand sqLiteCommand = new SQLiteCommand (sqLiteConnection);
          sqLiteCommand.CommandText = "CREATE TABLE IF NOT EXISTS Test (tick INTEGER NOT NULL, str VARCHAR(200) NOT NULL)";
          sqLiteCommand.ExecuteNonQuery ();

          while (_shouldStop != true)
          {
            Thread.Sleep (1000);

            if (DataHistory.SynchronizedQueue.Count < 1000)
            {
              continue;
            }

            using (var sqLiteTransaction = sqLiteConnection.BeginTransaction ())
            {
              sqLiteCommand.Connection = sqLiteConnection;
              sqLiteCommand.Transaction = sqLiteTransaction;
              int transactionCounter = 0;

              while( DataHistory.SynchronizedQueue.Count > 0 && transactionCounter < TransactionLimit)
              {          
                sqLiteCommand.CommandText = "INSERT INTO Test (tick, str) VALUES(1,'" + DataHistory.syncDequeue () + "')";
                sqLiteCommand.ExecuteNonQuery ();
                transactionCounter++;
              }
              sqLiteTransaction.Commit ();
              Thread.Sleep (1);
            }
          }
          sqLiteConnection.Close ();
        }).Start ();
    }

    public void Stop ()
    {
      _shouldStop = true;
    }
  }
}

EDIT:

 if (DataHistory.SynchronizedQueue.Count < 1000)
            {
              continue;
            }

und

  while( DataHistory.SynchronizedQueue.Count > 0 && transactionCounter < TransactionLimit)

muss ich nochmal abändern 😛

LG

30.07.2015 - 10:20 Uhr

Hallo,

im Rahmen meiner Masterarbeit arbeite ich zur Zeit an einem Geräteserver. Der Server holt sich Werte von verschiedenen Geräten wie SPS, Leistungsmessgerät, Sensoren usw. die über verschiedene Arten und Schnittstellen angebunden sind. Zum Beispiel USB, CAN, Flexray, TCP/IP.
Zum Server gehört ein Clients der definiert welche Werte von welchen Geräten er haben möchte und teilt dies dem Server mit. Der Server startet und verwaltet die GEräte und stellt dem Client die Werte zur Verfügung. Entweder direkt (eingebunden als DLL in Client App) oder über TCP/IP.
Die Datenrate mit der die Werte aus den Geräte gelesen und übertragen werden soll liegt bei einigen bei 1 oder 5 m/s meist eher 10m/s , der Datentype sollte i.d.R. ein primitiver Typ sein.

Nun möchte ich in meiner Server-Applikation ein Datenlogging/DataHistory anlegen. Dazu habe ich eine sycronisierte Queue angelegt in die Wert, Zeitpunkt und Geräte(Herkunft/Id abgelegt werden können. Nun würde ich mir einen Task anlegen der diese Queue ausließt und alles was darin ist persistent abspeichert.

Meine Anforderung:

  • wenig Performance fressend (um Datenraten einzuhalten)
  • große Datenmengen (Laufzeit des Servers über Monate)
  • alte Daten leicht löschbar (nur Werte der letzen x-Stunden/Tage)
  • leicht umgänglichen Datensystem (ungeschulte Person soll sich die Daten anschauen können und eventuell auch damit Arbeiten)
  • aufgetrenntes Speichern der Werte für verschiedene Geräte

Meine bisherigen Ideen sind:

  • SQLite (Erlaubt performante Transactions, gute Auftrennung(Tables),schon mit gearbeitet, für größere Datenmengen, gute Wartbarkait ,umgäglichkeit nur bedingt mit SQLite Browser (Installation notwendig))
  • mehrere asyncron beschriebene .data / .txt Dateien für jedes Gerät (sehr nativ,schon mit gearbeitet, schlecht wartbar auch wenn z.B. alle Stunde eine neue Datei angelgt wird, nicht so super für große Datenmengen, txt kann jeder öffnen )
  • Common Logging .Net API (gar keine Erfahrung, überhaupt sinnvoll?, Elefanten/Spatzen schießen)
  • XML (nativ, Auftrennung in einer Datei möglich, einigermassen umgänglich, Wartbarkeit auch ok)

Nun bin ich mir unsicher was ich nutzen sollte. Vorallem was die Performance angeht. Um den Punkt Umgänglichkeit zum umschiffen hab ich auch schon überlegt die Funktion einbaue aus den gespeicherten Werten eine Excel-Datei zu genieren. Am meisten bin ich von SQLite angetan, aber mein Betreuer aus oben genannten Gründen nicht 😜

Habt ihr noch andere Ideen? Oder Argumente pro SQLite?

Viele Grüße

01.06.2015 - 16:18 Uhr

Ich hatte diesen Artikel im MSDN gefunden:
https://msdn.microsoft.com/de-de/library/b8ytshk6%28v=vs.110%29.aspx

Aber dort wird im zweiten Beispiel Punkt zwei auch vorrausgesetzt, dass der spätere Typ (string und Example) zur Laufzeit bekannt ist.

01.06.2015 - 15:54 Uhr

Beschreib doch mal genau, was Du tun willst und welche Vorteile Du erwartest dann kann man die Anbieten, was man da machen kann.
Ich kann aus dem Konstrukt jedenfalls nicht erkenne, was Du vor hast und wozu das gut sein soll.


namespace DeviceComfortApp004
{
  class Channel<T> : ChannelBase
  {
    public T Value { get; set; }

    public override string ToString()
    {
      return "" + Value;
    }
  }
}

namespace DeviceComfortApp004
{
  class ChannelBase
  {
    public Address Address { get; set; }

    public string getChannelTyp()
    {
      return GetType().ToString();
    }

    public string getChannelAddress ()
    {
      return Address.ToString ();
    }
  }
}


namespace DeviceComfortApp004
{
  internal class Address
  {
    //Fields

    //Constructors
    public Address (int deviceTyp, int deviceNumber)
    {
      DeviceTyp = deviceTyp;
      DeviceNumber = deviceNumber;
    }

    //Methods
    public override string ToString()
    {
      return DeviceTyp + "." + DeviceNumber;
    }

    //Properties
    public int DeviceTyp { get; set; }
    public int DeviceNumber { get; set; }
  }
}

Diese Channels sollen verschiedene angeschlossene Geräte darstellen. Z.B. SPS, Usb-Geräte(serials Port mit Treiberbibliotheken) usw. Der User soll nun über die GUI eine neue Klasse erstellen können die die verschiedenen Channels beinhalten (mit CodeDome oder eher Roslyn). Die Klasse soll dann zum Beispiel so aussehen:


namespace DeviceComfortApp004
{
  class ApplicationChannels
  {
    //Fields
    public Channel<double> Voltage = new Channel<double>();
    public Channel<int> Length = new Channel<int>();
 
    //Constructor
    public ApplicationChannels()
    {
      Voltage.Address = new Address(0, 0);
      Length.Address  = new Address(0, 1);
    }

    //Methods

    //Properties
  }
}

Diese Channels will ich dann wiederum an Threads übergeben welche eine Queue mit zurückgebenen Werten der Geräte befüllt. Problem dabei ist ja, dass wenn ich einen generischen Typen einer Klasse übergeben will diese auch generisch sein muss. Also verwende ich meinen Basistypen von Channel ChannelBase als Übergabeparameter. Kann ich ja wieder zurück Konvertieren um an Value zu kommen.

Aber irgendwann muss ich ja auch wieder auf meine instanzieren Wert Value in der ChannelBase Instanz zugreifen und muss den tatsächlichen Typen wissen und eine Variable von genau diesem Typen angelegen können. Am besten ohne, dass ChannelThread selbst generisch sein muss... (siehe TRY/CATCH)


using System;
using System.Diagnostics;
using System.IO;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace DeviceComfortApp004
{
  class ChannelThread
  {
    //Gibt Wert von Channel zurück
    //generische Behandlung?

    private bool _shouldStop = false;
    public string value;
    private ChannelBase ChannelBase;
    private Stream Stream;
    private int Clock;
    

    public ChannelThread (ChannelBase channelBase, int clock)
    {
      ChannelBase = channelBase;
      Clock = clock;
    }

    public void TcpServerConnect(TcpClient tcpClient)
    {
      try
      {
        Stream = tcpClient.GetStream();
        Console.WriteLine("TCP Server connected \n");
        _shouldStop = false;

      }
      catch (Exception exception)
      {
        Console.WriteLine("TCP Server communication error\n" + exception.GetType());
      }
    }

    public void Run()
    {
      Stopwatch watch = new Stopwatch ();
      watch.Start ();

      while (!_shouldStop)
      {
        watch.Reset ();

        try
        {
          //HIER SOLL DER AKTUELLE WERT AUS DEM DEVICE 
          //THREAD GELESEN UND DANN GESENDET WERDEN!
          value = (Channel<HIER DOOF>)ChannelBase.Value.ToString ();

          Byte[] sendBytes = Encoding.ASCII.GetBytes(DateTime.Now.ToString() + " |> "+ value + "\r\n");
          Stream.Write (sendBytes, 0, sendBytes.Length);
        }
        catch (Exception exception)
        {
          Console.WriteLine ("TCP communication error\n" + exception.GetType ());
        }

        Thread.Sleep(Clock - (int)watch.ElapsedMilliseconds);
      }
    }

    public void ShouldStop()
    {
      _shouldStop = true;
    }
  }
}

01.06.2015 - 15:17 Uhr

Hallo,

kann der Type einer Instanz eines generischen Objekts weiter verwendet werden?

Also etwas in der Art wie dieses (nicht ausführbar!) :


class Program
  {
    static void Main()
    {
      List<int> l1 = new List<int> ();

      Type type = l1.GetType ();
      
      List<type> l2 = new List<type> ();

    }
  }

Eine Möglichkeit die ich sehe wäre mir l1.GetType().ToString.Equals("ein Datentyp") zurück geben zu lassen und für jeden möglichen Typen eine eigene Initialisierung für l2 anzulegen. Aber das finde ich etwas sehr umständlich. Gibt es eine elegantere Lösung?

Leider finde ich keine passenden Beispiele oder ich suche nach den falschen Stichworten.

04.08.2014 - 15:45 Uhr

Kabelbruch zwischen Board und Converter. Schande auf mein Haupt.

Bitte löschen.

04.08.2014 - 11:27 Uhr

Hallo,

ein Screenshot von HTerm im Anhang.

Habe gerade auf Grund eines Tips mal eine LED über RX und Ground geschaltet und dauer gesendet. Ebenso über Vcc und GND. Leider tut sich nix. Mich wundert auch, dass ich über VCC und GND keine 5V mit einem Multimeter messe. Weder am Board noch am TTL Ausgang. Wie gesagt hab ich alles in doppelter Ausführung. Also hab ich das auch mit zwei TTL Convertern getestet.

Wenn ich gerafft habe wie ich an dem Oscillokop das hier rum fliegt Bilder speicher lade ich das auch nochmal hoch.

Mein Dauersender:


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.IO.Ports;

namespace ConsoleApplication1
{
    class Program
    {
        //public static SerialPort serialPort;

        static void Main(string[] args)
        {
            Console.WriteLine("Press any Key to start Application");
            Console.ReadKey();

            try
            {
                SerialPort serialPort = new SerialPort();

                serialPort = new SerialPort("COM32", 38400, Parity.None, 8, StopBits.Two);
                serialPort.Open();
                Console.WriteLine("Port opend");

                byte[] GET_VERSION = { 0x00, 0xFF };
                int received;

                for (int i = 0; i < 100000;i++)
                {
                    serialPort.Write(GET_VERSION, 0, 2);
                    received = serialPort.ReadByte();

                    Console.WriteLine("received: " + received);

                    //System.Threading.Thread.Sleep(1);
                }

                serialPort.Close();
                Console.WriteLine("Hb627 closed!");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine("EXCEPTIO: " + ex.ToString());
                Console.ReadKey();
            }
        }
    }
}

Danke

04.08.2014 - 10:14 Uhr

Das hat doch nichts damit zu tun ob ein Byte gesendet wird, sondern wie das Byte Übertragen wird.

Die Daten werden doch als Data-Frames als [Startbits + x Datenbits + Stoppbits] übertragen. Sende ich einen Wert dessen Darstellung n Bits benötigt wird dieser seriell in n/x einzelne Sequenzen zerlegt gesendet. Ist das so richtig?

Hast du schon mal mit die Schnittstelle mit 7/N/2 initialisiert?

Kannst du mir das näher erklären. Durch Google werd ich nicht schlauer.

@#CPferdchen:
Da hast du aber einiges falsch verstanden.

Wenn du bei HTerm in die Eingabezeile 0x00 eingibst, werden 4Byte übertragen,
nämlich die bytewerte für 0, x, 0 und 0.

Mir ist klar, dass das 0x nur die übliche Markierung eines Hexwertes bedeutet. Ich sende also nur den eigentlichen Wert nach 0x.

Danke,
#CPferdchen

04.08.2014 - 00:22 Uhr

Dies ist nicht das Help Board für HTerm.

Entschuldigung. Ich war davon ausgegangen, dass im Smalltalk-Forum der inhaltliche Gegenstand eines Threads keine Rolle spielen würde.

Aber nur weil du irgendwas in Hex schreibst, wird davon nicht magisch ein echtes Byte das zur Seriellen schnittstelle gesendet wird.
Wenn du bytes senden willst, solltest du das auch tun.

Beim Inistialisieren des COM-Ports gebe ich doch 8bits als Data an. Also gehe ich mal davon aus das am Ende auch ein Byte gesendet wird.

03.08.2014 - 23:48 Uhr

Hallo,

Ich habe hier ein Motor-Driver Board MD25, dass ich über eine USB-RS232 Schnittstelle an eine TTL Converter angeschlossen habe. Die Pine des Converters sind der MD25 Technical Documentation
entsprechend angeschlossen:

Will ich nun über Hterm die in der MD25 Serial Documentation beschriebenen Kommandos ausführen passiert nix:

Ich sende in einem erst die 00 und dann z.B. die 26. Nun sollte ich eigentlich die Software-Version des Boards als Antwort erhalten. Auch auf andere Kommandos wird weder geantwortet noch mit Aktionen der Motoren reagiert.

RS232 Schnittstelle und TTL Converter funktionieren an einem STM32 Board hervorragend. Die Spannungquelle des MD25 ist eine Labornetzteil. Ich verwende diesen RS232-TTL Converter.

Hterm habe ich mit den verschiedenen Baudraten und Jumpersetting durch getestet. Die Kommandos als Hex gesendet.

Da ich von dem MD25 zwei Boards hier habe kann ich ausschließen, dass die beide kaputt sind. Ausserdem neu.

Jemand eine Idee was ich falsch mache?

LG,
Pferdchen

14.11.2013 - 12:47 Uhr

Oder meinst du, dass da auch Abweichungen auftreten könnten?

Ja

Es ist aber sehr wichtig, dass die eingestellte Frequenz so genau wie möglich eingehalten wird

Mit den Änderung wird sie zumindest im Millisekunden-Bereich eingehalten und du kannst später anhand des Zeitstempels das ganze überprüfen.

14.11.2013 - 11:26 Uhr

Hi,

würde dir noch folgende Änderungen ams Herz legen:


Stopwatch runtimeWatch = new Stopwatch();
runtimeWatch.Start();

while (true)
{
    runtimeWatch.Reset();
    Protokoll.WriteLine(Cursor.Position.X + "\t" + Cursor.Position.Y+"\t"+(Zeit.ElapsedTicks * 10000) / Stopwatch.Frequency;");
    i++;
               
    if (Console.KeyAvailable)
    {
          if (Console.ReadKey(true).KeyChar == 'b')
                 break;
    }

    Thread.Sleep(Wait-runtimeWatch.ElapsedMilliseconds);
}

  1. Die Durchlaufzeit mit einer weiteren Stopwatch messen und von der Sleeptime des Threads abziehen. Anderenfalls stimmt deine Frequenz nicht, da der Durchlauf des Codes innerhalb der while auch eine gewisse Zeit brauch. Was zu einer Abweichung deiner Frequenz führt. Aber Achtung: Wird Wait zu klein gewählt kann die Sleeptime negativ werden, was zu einem Laufzeitfehler führen würde! Also noch try/catch drum.

  2. Speicher einen Zeitstempel mit um deine Frequenztreue überprüfen zu können. Im Code sollte sich hier einer mit 1/10 ms ergeben (besser nochmal überprüfen, ist nicht von mir getestet) Guckst du Genaue Zeitfunktion

LG

12.11.2013 - 18:19 Uhr

Huhu,

Fand das ganze eine nette Übung für mich. Drum hab ich mich mal hingesetzt und ein Programm geschrieben das quasi das selbe ohne feste Frequenz macht.

Wie meine Vorredner schon meinten ist wirkliche Echtzeit mit C# nicht möglich. In der Enqueue Methode werden ein Timer Tick und dann x und y Koordinaten zwischen gespeichert, aber in dem Moment in dem X und Y ausgelesen werden stimmen sie ja schon mit dem Tick nicht mehr überein....

Nun scheinbar hab ich aber auch Mist gebaut, da irgendwie das MouseReader Thread nicht beendet wird. Hab ich mir da eine Race Condition o.Ä. erzeugt? Aber vielleicht nur ein kleiner Fehler den ich einfach nicht sehe??

Und meine eigene Frage: Ist ein solche Initialisierung des ReaderThreads im WriterThread eigentlich zulässig?


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MousePositionLogger
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Press any Key to start Mouseposition-Logging");
            Console.ReadKey();

            FileWriter fw = new FileWriter();
            Thread fwThread = new Thread(fw.DoWrite);

            fw.RequestStart();
            fwThread.Start();

            Console.WriteLine("Press any Key to stop Mouseposition-Logging");
            Console.ReadKey();

            fw.RequestStop();
            Console.WriteLine("Press any Key to stop Applikation");
            Console.ReadKey();

            Environment.Exit(0);
        }
    }
}


using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace MousePositionLogger
{
    class FileWriter
    {
        private volatile bool _shouldStop = true;
        int i = 0;

        MouseReader mr = new MouseReader();
        Thread mrThread;

        StreamWriter sw = new StreamWriter("C:\\temp\\messergebnisse.txt", true);
        long[] data = new long[3];

        public void DoWrite()
        {
            sw.WriteLine("Tick\t\tX\tY");

            mrThread = new Thread(mr.DoRead);
            
            mr.RequestStart();
            mrThread.Start();

            
            Thread.Sleep(20);
            
            Console.WriteLine("Begin Writing");

            while ((!_shouldStop) && mr.syncQueue.Count > 0)
            {
                if(_shouldStop)
                {
                    mr.RequestStop();
                    Console.WriteLine("a");
                }

                if(mr.syncQueue.Count > 0)
                {
                    data = mr.Dequeue();
                    i++;
                    sw.WriteLine(data[0] + "\t" + data[1] + "\t"+data[2]);
                }
                Thread.Sleep(1);
            }

            Console.WriteLine("Stop Writing after "+i+" operations");

            sw.Close();
        }

        public void RequestStop()
        {
            _shouldStop = true;
        }

        public void RequestStart()
        {
            _shouldStop = false;
        }

    }
}


using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ConsoleApplication1
{
    class MouseReader
    {
        private volatile bool _shouldStop = true;
        int i = 0;
        public Queue dataQueue = new Queue();
        public Queue syncQueue;

        Stopwatch watch = new Stopwatch();

        private void Enqueue()
        {
            long[] data = new long[3];

            lock (this)
            {
               
                data[0] = (watch.ElapsedTicks * 10000) / Stopwatch.Frequency;  //1/10 ms
                data[1] = Cursor.Position.X;
                data[2] = Cursor.Position.Y;
                i++;
                syncQueue.Enqueue(data);
                //Console.WriteLine(i+"\t"+data[0] + "\t" + data[1] + "\t" + data[2]);

                Monitor.Pulse(this);
            }
        }

        public long[] Dequeue()
        {
            lock (this)
            {
                while (syncQueue.Count == 0)
                {
                    Monitor.Wait(this);
                }

                return (long[]) syncQueue.Dequeue();
            }
        }

        public void DoRead()
        {
            syncQueue = Queue.Synchronized(dataQueue);
            watch.Start();

            Console.WriteLine("Begin Reading");

            while (!_shouldStop)
            {
                Enqueue();
                Thread.Sleep(1);
            }

            Console.WriteLine("Stop Reading after "+watch.ElapsedMilliseconds+" [ms] and "+i+" operations");
            watch.Stop();
        }

        public void RequestStop()
        {
            _shouldStop = true;
        }

        public void RequestStart()
        {
            _shouldStop = false;
        }
    }
}

EDIT: Mit Events wär das ganze eigentlich sogar viel einfacher oder? Also das was meine Enqueue Methode macht in entsprechende MouseMoveEvent-Methode und das ganze SyncronizedQueue Geraffel mit in die FileWriter class und man hätte nur den einen Thread und weniger Operationen, wenn die Maus nicht bewegt wird.

Und @Souroba
Was willst du eigentlich damit machen?

LG

19.01.2012 - 08:55 Uhr

Könnte es vielleicht besser sein, statt n aufzeichnende Threads mit Qeues und einem Qeues abarbeitenden SQL Thread, nur n aufzeichnende Threads zu haben die eine globale Connection zur DB und jeweils eigene Commands haben. So könnte ich in den jeweiligen DoWork Methoden die Transaction Parameter selbst als meine Qeues nutzen.

Dadurch würde im Grunde der selbe Code durchlaufen werden, aber ich würde mir das Speichern in LinkedList' Qeues, so wie es zur Zeit umgesetzt ist, sparen.

Was haltet ihr von der Idee? Ist ein bisschen Arbeit, darum wäre es nett, wenn mir jemand eine Einschätzung geben könnte ob das bedeutet schneller funktionieren könnte.

18.01.2012 - 23:32 Uhr

Meine Hochschule ist so nett und stellt mir im Downloadbereich Ulimate zur Verfügung. Naja, Ist ja auch von meinen Studiengebühren bezahlt 😉

Also meint ihr, dass das was ich da oben geschrieben habe eine gescheite Lösung meines Problems ist? Und es geht nicht schneller?

Ich hatte zuvor das ganze Versucht mit nur einem Command umzusetzten. In dem ich für jedes Qeue eine eigene Transaction begonnen habe und diese, wenn das jeweilige Qeue <1 war, mit .Commt() beendet und dann die Parameter.Reset() Methode genutzt habe, um die Parameter für die nächste Transaction neu beschreiben zu können.

Das machte allerdings keine wesentlich Unterschied der Performance (Glaube ich).

Gibt es also keine Möglichkeit im Sqlite nur eine Transaction mit nur einem Command ausführen zu können? (In der Hoffnung, dass dies die Resoursen schont).

18.01.2012 - 20:20 Uhr

Enqeue ist ebenfalls innerhalb der Methode gelockt.
Hab mir dacht son lock() in jeder DoWork Methode könnte nicht schaden. Genau genommen in ALLEN DoWork Methoden. Also auch denen die die Qeues füttern.
Hat Visual Studio 2010 einen Profiler?

Ich schaffe nur um die 3000, allerdings sei gesagt, dass ich keine Integer-Zahlen speicher, sondern u.a. Can-Bus Nachrichten, NMEA Sentences (GPS) und 17 Byte eines Datenerfassungsmoduls.

Ist SQLite überhaupt die Richtige Wahl für meine Applikation?

18.01.2012 - 16:13 Uhr

@FZelle
Habe es mit und ohne Compress probiert und keine Performanceänderung bemerken können, also hab ich es einfach so gelassen wie Rollerfreak2 es in seinem Thread geschrieben hat. --> Also ändern?
Wg byte0: Ich hatte meine Code etwas gekürzt und verallgemeinert. Dabei scheinbar eine Stelle nicht mitgeändert. Ist aber jetz überarbeitet.

@GarlandGreene
Meine GPS Deqeue Methode sieht zum Beispiel so aus:


        public GpsData Deqeue()
        {
            lock (this)
            {
                GpsData data = null;

                if (DataQeue.Count > 1)
                {
                    try
                    {
                        data = DataQeue.First();
                        DataQeue.RemoveFirst();
                    }
                    catch (InvalidOperationException)
                    {
                        f.ShowStatus("DeqeueErrorGPS InvalidOperationException");
                    }
                    catch (ArgumentNullException)
                    {
                        f.ShowStatus("DeqeueErrorGPS ArgumentNullException");
                    }
                }
                return data;
            }
        }

Hat also ein lock. Ist das so nicht Threadsafe?

Mit der Performance des Rechners ist mir klar. Das System auf dem das Programm später laufen soll ist ein Embedded PC mit Intel Core Duo P8600 @2,4 GHz und einer Solid State Disk. Sollte also schon schneller laufen als auf meinem Entwicklungssystem.

Meine Frage ist jetz kann ich dieses Snippet noch schneller machen?

18.01.2012 - 13:50 Uhr

verwendetes Datenbanksystem: <SQLite>

Hallo,

ich habe mal eine Frage bezüglich der Perfomance einer Sqlite Datenbank mit Transactions in verschiedene Tables.
Meine eigentliches Problem ist, dass ich mehrere Serielle Ports in Threads auslese, welche dann die empfangenen Daten in LinkedLists (Qeues) zwischen speichern. Die DoWork Methode meines SQL Threads nimmt sich dann diese Daten der Qeues und schreibt sie in die Db weg.

Nach anfänglich erheblichen Performenceproblemen bin ich auf diesen Beitrag von rollerfreak2 gestossen und konnte mit Transactions die Geschwindigkeit deutlich erhöhen.

Rollerfreak hatte geschrieben, dass er 100000 Nachrichten in 1,7 Sekunden schreiben kann. Ich schaffe nur um die 3000, allerdings sei gesagt, dass ich keine Integer-Zahlen speicher, sondern u.a. Can-Bus Nachrichten, NMEA Sentences (GPS) und 17 Byte eines Datenerfassungsmoduls.

Ich habe mein Code mal etwas verallgemeinert abgeschrieben, wobei die Threads A und B für meine "seriellen" Threads stehen sollen.

Schneller, schneller, schneller brauch ich. Jemand eine Idee und geht es nicht auch ohne zwei Commands?


        public void DoWork()
        {
            using (SQLiteConnection connection = new SQLiteConnection("Data Source=" + f.getDbname() + ".db;Version=3;New=True; Compress=True; UTF8Encoding=True;"))
            {
                A a= new A();
                B b= new B();

                Thread aThread = new Thread(a.DoWork);
                Thread bThread = new Thread(b.DoWork);

                connection.Open();
                SQLiteCommand aCommand = connection.CreateCommand();
                SQLiteCommand bCommand = connection.CreateCommand();

                aCommand.CommandText = "CREATE TABLE IF NOT EXISTS A (a INTEGER);";
                aCommand.ExecuteNonQuery();
                bCommand.CommandText = "CREATE TABLE IF NOT EXISTS B (b VARCHAR(100);";
                bCommand.ExecuteNonQuery();

                SQLiteParameter aParameter_list = aCommand.CreateParameter();
                SQLiteParameter bParameter_list = bCommand.CreateParameter();

                aCommand.CommandText = "INSERT INTO A (a) VALUES (@a);";
                aCommand.Parameters.Add("@a", DbType.Int32);

                bCommand.CommandText = "INSERT INTO B (b) VALUES (@b);";
                bCommand.Parameters.Add("@b", DbType.String);

                 while (!_shouldStop)
                 {
                     lock (this)
                     {
                            aCommand.Transaction = connection.BeginTransaction();    
                            
                            while(a.DataQeue.Count() > 1 & !_shouldStop)
                            {
                                try
                                {
                                    aData = a.Deqeue();

                                    if (aData != null)
                                    {
                                        aCommand.Parameters[0].Value = aData.Something;
                                        aCommand.ExecuteNonQuery();
                                    }                                       
                                }
                                catch (InvalidOperationException)
                                {
                                    f.ShowStatus("Invalid Operation in GPS Transaction");
                                }
                                catch (SQLiteException)
                                {
                                    f.ShowStatus("SQLite Error in GPS Transaction");
                                }
                            }
                            aCommand.Transaction.Commit();
                        }

                        bCommand.Transaction = connection.BeginTransaction();    
                            
                        while(b.DataQeue.Count() > 1 & !_shouldStop)
                        {
                            try
                            {
                                bData = b.Deqeue();

                                if (bData != null)
                                {
                                    bCommand.Parameters[0].Value = bData.Something;
                                    bCommand.ExecuteNonQuery();
                                }                                       
                            }
                            catch (InvalidOperationException)
                            {
                                f.ShowStatus("Invalid Operation in CAN Transaction");
                            }
                            catch (SQLiteException)
                            {
                                f.ShowStatus("SQLite Error in CAN Transaction");
                            }
                        }
                        bCommand.Transaction.Commit();
                 }
            }
        }

Grüße,
#CPferdchen

08.11.2011 - 18:22 Uhr

Ahoi,

ich habe, um den Umgang mit Seriellen Schnittstelllen zu lernen, ein kleines Beispielprogramm gebastellt. Eine Forms-Anwendung mit einem Button zur Initialisierung und Öffnen eines neuen seriellen Ports. Einen weiteren der eine Methode als Thread aufruft, welche den Port ausließt und einen, den Problembutton, der die serielle Anbindung schießt, den Thread erst mit Join stoppen soll und dann mit Abort beenden.

Der Teilnehmer am Port ist eine USB GPS Reciver der NMEA Signale ausgibt.
Alle die serielle Schnittstelle betreffenden Funktionnen sind in einer gesonderten Klasse untergebracht. Die Klasse enthält die Methode DoWork(), welche durch einen Thread im Form-Thread aufgerufen wird. Desweiteren zwei Methode um eine Endlossschleife in DoWork von aussen verlassen und betretten zu können. Also so wie man es unzähligen Beispielen findet...

Im Folgenden die oben beschriebene Klasse wobei ich die Funktionalität zum Öffnen und Lesen jetz mal weg gelassen habe:


using System.Threading;
using System.IO.Ports;

namespace GPS__Test_0._1
{
    class GPS
    {
        Form1 f;
        SerialPort serialPort;

        private volatile bool _shouldStop = false;


         public GPS(Form1 f)
        {
            this.f = f;
        }

        public void Unint()
        {
            try
            {
                if (serialPort.IsOpen)
                {
                    serialPort.Close();
                }
            }
            catch (Exception ex)
            {
                f.SetText("Unint Exception: " + ex);
            }
        }

        public void DoWork()
        {
            try
            {
                while (true)
                {
                    if (_shouldStop)
                        break;
                    
                    string msg = serialPort.ReadLine();

                    f.SetText(msg);

                    Thread.Sleep(100);

                   
                }
            }
            catch (StackOverflowException SOex)
            {
                f.SetText("DoWork Exception: " + SOex);
            }
            catch(Exception ex)
            {
                f.SetText("DoWork Exception: " + ex);
            }
        }

        public void RequestStop()
        {
            _shouldStop = true;
        }

        public void RequestStart()
        {
            _shouldStop = false;
        }
}

Meine Klasse Form


namespace GPS__Test_0._1
{
    public partial class Form1 : Form
    {
        GPS gps;
        Thread gpsThread;

        public Form1()
        {
            InitializeComponent();
            gps = new GPS(this);
        }

        delegate void SetTextCallback(string text);

        public void SetText(string text)
        {
            if (lbxStatus.InvokeRequired)
            {
                SetTextCallback d = new SetTextCallback(SetText);
                this.Invoke(d, new object[] { text });
            }
            else
            {
                this.lbxStatus.Items.Add(text);

                lbxStatus.SelectedIndex = lbxStatus.Items.Count - 1;
            }
        }

         private void btnInit_Click(object sender, EventArgs e)
        {
            gps.RequestStart();
            SetText("Request started");
            gps.Init("COM6");
            SetText("Init with COM6 called");
        }

        private void btnStart_Click(object sender, EventArgs e)
        {
            try
            {
                gps.RequestStart();
                gpsThread = new Thread(gps.DoWork);
                gpsThread.Start();
            }
            catch (Exception ex)
            {
                SetText("GPS Thread starting: " + ex);
            }
        }

        private void btnStop_Click(object sender, EventArgs e)
        {
            
            try
            {
                gps.RequestStop();
                gpsThread.Join();
                gpsThread.Abort();
                gps.Unint();
            }
            catch (Exception ex)
            {
                SetText("GPS Thread stopping: " + ex);
            }
            lbxStatus.Items.Add("gpsThread stopped");
        }
    }
}

Wenn ich mich nun in der Situation befinde, dass meine Port geöffnet ist, die NMEA Signale in meiner ListBox ausgegeben werde und ich den Stop Button drücke gefriert mein Fenster. Debuggen hat gezeigt es liegt an

gpsThread.Join();

Warum?

1.
Hat es mit der Form1.SetText() Methode zu tun? Ist das so überhaupt Threadsafe oder überhaupt sauber Objektorientirt? Das der Absturz daher rüht, dass noch Nachrichten auf der ListBox mit SetText ausgeben werden sollen, aber das irgendwie nicht geht und so das gpsThread ewig auf das FormThread wartet?

oder 2.
In der MSDN Lib wird Join mit
"Blockiert den aufrufenden Thread, bis ein Thread beendet wird, während das Standard-COM- und das SendMessage-Pumping fortgesetzt werden. " beschrieben.
Hat dann damit zu tun, dass der Thread nicht gestoppt werden kann, weil noch Nachrichten am COM Port ankommen. Und wenn ja, warum, da ich den Port eigentlich schon geschlossen habe, wenn ich Join ausführe.

Ich hoffe ihr könnt einem Neuling weiter helfen.

Mit freundlichen Grüßen,
Cpferdchen