Système.IO.Les Ports.SerialPort et le Multithreading
J'ai quelques SerialPort code qui a constamment besoin de lire des données à partir d'une interface série (par exemple COM1). Mais cela semble être beaucoup de temps CPU et si l'utilisateur déplace la fenêtre ou d'un lot de données est affiché à la fenêtre (tels que les octets reçus sur la ligne série) puis la communication se foiré.
Considérant le code suivant:
void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
byte[] buffer = new byte[port.ReadBufferSize];
var count = 0;
try
{
count = port.Read(buffer, 0, buffer.Length);
}
catch (Exception ex)
{
Console.Write(ex.ToString());
}
if (count == 0)
return;
//Pass the data to the IDataCollector, if response != null an entire frame has been received
var response = collector.Collect(buffer.GetSubByteArray(0, count));
if (response != null)
{
this.OnDataReceived(response);
}
Le code doit être collecté dans le flux de données est constante
et les données doivent être analysées (images/paquets).
port = new SerialPort();
//Port configuration code here...
this.collector = dataCollector;
//Event handlers
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
port.Open();
Si il n'y a aucune interaction de l'utilisateur et rien n'a été ajouté à la fenêtre,
cela fonctionne bien mais dès qu'il y a une interaction de communication devient vraiment foiré.
Délais d'attente se produisent etc....
Par exemple, cela crée tout:
Dispatcher.BeginInvoke(new Action(() =>
{
var builder = new StringBuilder();
foreach (var r in data)
{
builder.AppendFormat("0x{0:X} ", r);
}
builder.Append("\n\n");
txtHexDump.AppendText(builder.ToString());
txtHexDump.ScrollToEnd();
}),System.Windows.Threading.DispatcherPriority.ContextIdle);
});
Mais même de simples appels à log4net causer des problèmes.
Existe-il des meilleures pratiques pour optimiser SerialPort communication
ou quelqu'un peut-il me dire ce que je fais mal...
Mise à jour:
Dans le cas ci-dessus n'a pas beaucoup de sens. J'ai fait un très simple (et stupide) petit exemple:
class Program
{
static void Main(string[] args)
{
var server = new BackgroundWorker();
server.DoWork += new DoWorkEventHandler(server_DoWork);
server.RunWorkerAsync();
var port = new SerialPort();
port.PortName = "COM2";
port.Open();
string input = "";
Console.WriteLine("Client on COM2: {0}", Thread.CurrentThread.ManagedThreadId);
while (input != "/quit")
{
input = Console.ReadLine();
if (input != "/quit")
{
var data = ASCIIEncoding.ASCII.GetBytes(input);
port.Write(data, 0, data.Length);
}
}
port.Close();
port.Dispose();
}
static void server_DoWork(object sender, DoWorkEventArgs e)
{
Console.WriteLine("Listening on COM1: {0}", Thread.CurrentThread.ManagedThreadId);
var port = new SerialPort();
port.PortName = "COM1";
port.Open();
port.ReceivedBytesThreshold = 15;
port.DataReceived += new SerialDataReceivedEventHandler(port_DataReceived);
}
static void port_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
var port = (SerialPort)sender;
int count = 0;
byte[] buffer = new byte[port.ReadBufferSize];
count = ((SerialPort)sender).Read(buffer, 0, buffer.Length);
string echo = ASCIIEncoding.ASCII.GetString(buffer,0,count);
Console.WriteLine("-->{1} {0}", echo, Thread.CurrentThread.ManagedThreadId);
}
}
Le résultat pourrait ressembler à ceci:
À l'écoute sur le port COM1: 6
Client sur le port COM2: 10
C'est un exemple de données que j'envoie
---> 6 Ceci est un exemple de données que j'envoie
Donc, la lecture de données à partir du port qui se passe sur le thread principal....
Cela peut-il être partie de ce qui est la cause de mes problèmes?
Le code ci-dessus est appelée lorsque l'IDataCollector déclenche l'événement pour indiquer qu'il a recueilli une image complète de données. C'est dans le WPF, Windows. Mais même si vous ajoutez du journal.Warn(".....") énoncés dans le code de la IDataTransportServer ou IDataCollector implémentations de la SerialCommunication se fait défoncer... Les "données" est tout simplement ce que nous avons lu à partir de la ligne de série: mémoire tampon.GetSubByteArray(0, count)
Juste au sujet de n'importe quel code sera la cause de la communication Série à aller mal...
OriginalL'auteur TimothyP | 2009-06-10
Vous devez vous connecter pour publier un commentaire.
Votre dernière conclusion, que l'événement se déroule sur le thread Principal peut ne pas être vrai pour les applications Windows. Ne pas tester cela dans une Console.
La bonne façon de régler ce est:
mis un tampon assez grand, bien que le minimum de 4096 est généralement Ok
définir la ReceivedBytesThreshold aussi haut que tolérable (et de le faire avant le Open())
faire aussi peu que possible dans l'événement, passer le
les données à une File d'attente ou MemoryStream si vous avez besoin de plus de temps
OriginalL'auteur Henk Holterman
Je suis surpris de pas pris de cet. La classe SerialPort utilise son propre thread lors de l'utilisation de la DataReceived événement. Cela signifie que si l'abonné, par exemple, est l'accès de tous les éléments de Formulaire, il doit être fait avec une Invoquer, ou méthode BeginInvoke(s). Sinon, vous vous retrouvez avec une croix fil de l'opération. Dans les anciennes versions de .net, ce serait aller inaperçu avec un comportement imprévisible (en fonction du CPU cores dans le PC) et dans les versions ultérieures, doit lever une exception.
OriginalL'auteur dave
Que vous devez réécrire port_DataReceived procédure pour lire les données jusqu'à ce port.BytesToRead est supérieure à zéro, comme ceci:
Aussi je vous recommande de vous suffit d'insérer les données dans la file d'attente de la liste dans la procédure port_DataReceived, et effectuer le traitement des données dans le thread séparé.
Attend, où, à l'intérieur de port_DataReceived() fonction ou port_DataReceived() n'est pas appelée ? Il y a quelques chose que je n'ai pas écrit dans le message original: - port_DataReceived est fonction de rappel et il s'exécute dans son propre thread. Il est appelé à chaque fois qu'il y a certaines données écrites sur le port COM. MAIS l'appel à port_DataReceived fonction peut être a eu des ratés dans le cas lorsque de nouvelles données sont écrites sur le port COM et vous êtes n'a toujours pas de retour de l'appel précédent à savoir le traitement des données à l'intérieur de la fonction, qui est la façon dont il est important de vérifier port.BytesToRead bien avant de vous quittez la fonction.
OriginalL'auteur Gordon Freeman
La solution classique est d'avoir un tampon FIFO. S'assurer que la taille de la FIFO est suffisamment grand pour traiter tout cas critique où il y a beaucoup de commentaires et le processeur bloc est en train d'être prises.
Vous pourriez même avoir un 2-système de mémoire tampon:
OriginalL'auteur Paul Nathan