myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Entwicklung » Basistechnologien und allgemeine .NET-Klassen » [gelöst] Zeitintensive Messhardware-Aufgabe -> Task, Thead oder doch Backgroundworker
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

[gelöst] Zeitintensive Messhardware-Aufgabe -> Task, Thead oder doch Backgroundworker

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
ill_son ill_son ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.09.2009
Beiträge: 174
Entwicklungsumgebung: Visual Studio 2013
Herkunft: Leipzig


ill_son ist offline

[gelöst] Zeitintensive Messhardware-Aufgabe -> Task, Thead oder doch Backgroundworker

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

in meiner Anwendung soll eine Kommunikation mit einer Messhardware stattfinden. Das Ganze sieht so aus, dass nacheinander verschiedene Signale erzeugt werden und jeweils eine Messung durchgeführt wird. Gesamtdauer etwa halbe bis eine Minute. Zwischendurch wären Statusmeldungen mit Zwischenergebnissen ganz nett. Jetzt stellt sich mir die Frage der Implementierung. Thread mit entsprechenden Events oder await Task mit Progress? Ich habe mal irgendwo gelesen, Backgroundworker sei seit async Task "obsolet". Aber mal grundsätzlich, wann Thread und wann Task?

Grüße, Alex

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von ill_son am 06.11.2019 14:38.

05.11.2019 09:54 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
witte
myCSharp.de-Mitglied

Dabei seit: 03.09.2010
Beiträge: 826


witte ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Wäre ne schöne Aufgabe für ReactiveExtensions.
05.11.2019 10:16 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Alf Ator
myCSharp.de-Mitglied

avatar-586.gif


Dabei seit: 30.10.2007
Beiträge: 591
Entwicklungsumgebung: VS2005 / VS2008


Alf Ator ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo ill_son

Benutze Tasks mit async/await, es sei denn du weißt, dass du für einen bestimmten Anwendungsfall etwas anders brauchst.

Ansonsten das was witte gesagt hat: "Wäre ne schöne Aufgabe für ReactiveExtensions."

Gruß
Alf
05.11.2019 10:47 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
gfoidl gfoidl ist männlich
myCSharp.de-Team

avatar-2894.jpg


Dabei seit: 07.06.2009
Beiträge: 6.600
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo ill_son,

Zitat:
grundsätzlich, wann Thread und wann Task?

Grundsätzlich (;-)) hatten wir diese Frage schon öfters, daher hier nur kurz der Rest bitte via  (Foren-)suche.

Thread ist relativ hardwarenahe und führt einen bestimmten Code aus.
Für kurze Aufgaben (höchsten ein paar Sekunden) wird ein Thread idealerweise aus dem ThreadPool verwendet, da das Erstellen eines Threads aufwändig ist.
Für längere Aufgaben sollte der ThreadPool vermieden werden und ein eigenständiger Thread verwendet werden. Dies v.a. da der ThreadPool für solche Anforderungen nicht konzipiert wurde und somit nicht optimal arbeiten kann.

Task ist eine Abstraktion, die auf Threads aufbaut, und es dem Benutzer vereinfachen soll mit asynchronen Aufgaben zu arbeiten. Dies v.a. durch die mit C# 5 eingeführten Schlüsselwörter async / await und durch etliche API im Framework die mit Task arbeiten.

mfG Gü
05.11.2019 11:54 Beiträge des Benutzers | zu Buddylist hinzufügen
ill_son ill_son ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.09.2009
Beiträge: 174
Entwicklungsumgebung: Visual Studio 2013
Herkunft: Leipzig

Themenstarter Thema begonnen von ill_son

ill_son ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

danke für Eure Antworten.

@gfoidl: Das heißt für mich, dass ich, anders als Alf sagt, doch besser einen eigenen Thread erstellen sollte, denn wie  hier beschrieben, nutzen Tasks den Thread Pool. Stimmt das so?

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von ill_son am 05.11.2019 14:13.

05.11.2019 14:03 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Abt
myCSharp.de-Team

avatar-4119.png


Dabei seit: 20.07.2008
Beiträge: 13.172
Herkunft: Stuttgart/Stockholm


Abt ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Schau Dir mal an, was Threads sind und was Tasks sind.

Zitat von gfoidl:
Zitat:
grundsätzlich, wann Thread und wann Task?

Grundsätzlich (;-)) hatten wir diese Frage schon öfters, daher hier nur kurz der Rest bitte via  (Foren-)suche.

Es gibt in .NET kaum noch einen Grund auf Threads zu setzen, weil prinzipiell fast alles mit Tasks umsetzbar ist.
In Deinem Fall hast Du einfach nur eine Long Running Operation, die Du - so wie ich das sehe - mit einem Long Running Task problemlos laufen lassen kannst.
Und dafür gibts Task.Run() (verwendet den ThreadPool, was in meinen Augen aber hier im Grunde erstmal egal ist).
05.11.2019 14:32 Beiträge des Benutzers | zu Buddylist hinzufügen
gfoidl gfoidl ist männlich
myCSharp.de-Team

avatar-2894.jpg


Dabei seit: 07.06.2009
Beiträge: 6.600
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo ill_son,

Zitat:
nutzen Tasks den Thread Pool. Stimmt das so?

Bedingt.

Task.Run, Task.Factorry.StartNew nutzen standardmäßig den TaskScheduler.Default und der arbeitet mit dem ThreadPool.
Es kann auch ein anderer TaskScheduler verwendet werden, der mit dem ThreadPool nichts zu tun hat.
Bei StartNew hängt es auch von den Flags, die der Fabrik übergeben werden, ab ob der Task im ThreadPool landet od. ein dezidierter Thread (TaskCreationOptions.LongRunning) erstellt wird.

Task.ContinueWith, async / await können ggf. auch auf den ThreadPool verzichten, falls z.B. bei await der Task bereits fertig ist, dann wird einfach im aktuellen Thread fortgefahren. Od. bei I/O-Abschlussthreads wird der (CPU-) ThreadPool auch nicht verwendet.

Weiters lassen sich Tasks mit TaskCreationSource<T> erstellen, dabei wird ganz auf den ThreadPool verzichtet.

Und das einfachste zum Schluss: Task<int> task = Task.FromResult(42); ist ein gültiger Task, der ebenfalls ohne ThreadPool auskommt.

Hab mir deinen Link nicht angeschaut, aber ich hoffe dass auf diese Details zumindest hingewiesen wurde ;-)

Zitat:
Das heißt für mich, ... doch besser einen eigenen Thread erstellen sollte

Ich würde es schon via Task abhandeln, nur ob der ThreadPool bzw. ein eigenständiger Thread (den der Task kapselt) verwendet wird weiß ich (noch) nicht.

Zitat:
verschiedene Signale erzeugt werden und jeweils eine Messung durchgeführt wird. Gesamtdauer etwa halbe bis eine Minute. Zwischendurch wären Statusmeldungen mit Zwischenergebnissen ganz nett.

Erzähl mal ein bischer mehr über den Aufbau.
Wie wird die Messung angestossen / durchgeführt?
Wie wird das Messergebnis erhalten?
Gibt die Messhardware Statusmeldungen zurück od. meinst du einfach in der UI einen Spinner, etc. anzeigen?
Wird mit einem bestimmte Protokoll (z.B. auf Basis TCP) mit der Messhardware kommuniziert?

Je nachdem gibt es u.U. elegantere Lösungen als einfach einen Task / Thread zu verwenden.

mfG Gü
05.11.2019 16:52 Beiträge des Benutzers | zu Buddylist hinzufügen
ill_son ill_son ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.09.2009
Beiträge: 174
Entwicklungsumgebung: Visual Studio 2013
Herkunft: Leipzig

Themenstarter Thema begonnen von ill_son

ill_son ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo gfoidl,

erst einmal danke für den ausführlichen Exkurs.

ich arbeite mit dem DAQmx von National Instruments, wenn dir das ein Begriff ist. Dieser unterstützt auch .NET. Ich habe eine Messkarte, mit dieser erzeuge ich einen Sinus unterschiedlicher Frequenz (Sweep) und messe für jede Frequenz einige Perioden. Das Erzeugen des Signals benötigt kaum Zeit, weil es auf der Karte passiert. Man muss den Vorgang nur initialisieren, aber da die Frequenzen recht niedrig sind, benötigt die Funktion, die die Messwerte holt ihre Zeit. Daten werden als double[,] zurückgegeben.

Im Prinzip durchlaufe ich eine for-Schleife derart:

C#-Code:
public async Task<List<FrequencyStepResult>> Sweep(CancellationTokenSource cts, IProgress<FrequencyStepResult> progress)
{
    List<FrequencyStepResult> result = new List<FrequencyStepResult>();
    if (IsHardwareReady)
        await Task.Run(() =>
        {
            for (int i = 0; i < FrequencySteps.Count && !cts.IsCancellationRequested; i++)
            {
                 FrequencyStepResult stepResult = PerformFrequencyStep(OutputMagnitude, FrequencySteps[i]);
                 result.Add(stepResult);
                 progress?.Report(stepResult);
             }
       });

    return result;
}

private FrequencyStepResult PerformFrequencyStep(double magnitude, double frequency)
{
      double duration = 5 / frequency; //at leat 5 periodes
      if (duration < 1)
           duration = 1;

      int sampleRate = (int)(20 * frequency);
      if (sampleRate < 1000)
           sampleRate = 1000;

      int samples = (int)Math.Ceiling(duration * sampleRate);

       _DAQmx.StartSinusOutput(magnitude, frequency);

       double[,] data = _DAQmx.ReadSequence(sampleRate, samples);

       _DAQmx.StopOutput();

       return new FrequencyStepResult(frequency, data);
}

Grüße, Alex
05.11.2019 17:45 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
gfoidl gfoidl ist männlich
myCSharp.de-Team

avatar-2894.jpg


Dabei seit: 07.06.2009
Beiträge: 6.600
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo ill_son,

welcher Typ ist _DAQmx genau?
Ein paar Methoden von DAQmx unterstützen asynchrone Vorgänge (nach dem (alten)  Asynchronous Programming Model (APM)), darauf lässt sich idealerweise aufbauen.

Schade dass DAQmx .NET Core 3.0 nicht unterstützt, denn mit "async streams" (IAsyncEnumerable) wäre das angenehm zu lösen).

mfG Gü
05.11.2019 18:28 Beiträge des Benutzers | zu Buddylist hinzufügen
ill_son ill_son ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.09.2009
Beiträge: 174
Entwicklungsumgebung: Visual Studio 2013
Herkunft: Leipzig

Themenstarter Thema begonnen von ill_son

ill_son ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo gfoidl,

_Daqmx ist die Instanz einer Klasse, die ich geschrieben habe. Sie kapselt den Zugriff auf die Karte und stellt mir die Funktionalitäten bereit, die ich für die Messung benötige. Unter DAQmx und APM habe ich auf der Seite von  NI was gefunden. Ich schaue mir das mal an, vielleicht ist das ein Einstieg.

Grüße, Alex

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von ill_son am 06.11.2019 09:25.

06.11.2019 09:20 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
gfoidl gfoidl ist männlich
myCSharp.de-Team

avatar-2894.jpg


Dabei seit: 07.06.2009
Beiträge: 6.600
Entwicklungsumgebung: VS 2019
Herkunft: Waidring


gfoidl ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo ill_son,

ja genau so gehts (nicht nur vom Code her, sonder auch wegen deiner Recherche dazu Daumen hoch ).

Mit  TPL and Traditional .NET Framework Asynchronous Programming lässt sich das dann in einem Adapter schön kapseln. Ich stell mir das grob so vor:

C#-Code:
public interface IMeasureDevice
{
    Task WaitForHardwareReadyAsync();
    Task StartSinusOutputAsync(double magnitude, double frequency);
    Task<double[,]> ReadSequenceAsync(int sampelRate, int sampleCount);
    Task StopOutputAsync();
}

public class Wobbler
{
    private const int MinPeriodCount = 5;
    private const int SampleRate     = 20;

    private readonly IMeasureDevice _measureDevice;

    public Wobbler(IMeasureDevice measureDevice)
    {
        _measureDevice = measureDevice ?? throw new ArgumentNullException(nameof(measureDevice));
    }

    public async Task<IReadOnlyCollection<FrequencyStepResult>> SweepAsync(
        double                         magnitude,
        IEnumerable<double>            frequencySteps,
        IProgress<FrequencyStepResult> progress,
        CancellationToken              ct = default)
    {
        var result = new List<FrequencyStepResult>();

        await _measureDevice.WaitForHardwareReadyAsync().ConfigureAwait(false);

        foreach (double frequency in frequencySteps)
        {
            if (ct.IsCancellationRequested)
                break;

            var frequencyResult = await this.PerformFrequencyStep(magnitude, frequency, ct).ConfigureAwait(false);
            result.Add(frequencyResult);
            progress?.Report(frequencyResult);
        }

        return result.AsReadOnly();
    }

    private async Task<FrequencyStepResult> PerformFrequencyStep(double magnitude, double frequency, CancellationToken ct)
    {
        double duration = Math.Max(1, MinPeriodCount / frequency);
        int sampleRate  = Math.Max(1000, (int)(SampleRate * frequency));
        int sampleCount = (int)Math.Ceiling(sampleRate * duration);

        await _measureDevice.StartSinusOutputAsync(magnitude, frequency).ConfigureAwait(false);
        double[,] data = await _measureDevice.ReadSequenceAsync(sampleRate, sampleCount).ConfigureAwait(false);
        await _measureDevice.StopOutputAsync().ConfigureAwait(false);

        return new FrequencyStepResult(frequency, data);
    }
}

public readonly struct FrequencyStepResult
{
    public double Frequency { get; }
    public double[,] Data   { get; }

    public FrequencyStepResult(double frequency, double[,] data)
    {
        if (frequency < 0) throw new ArgumentOutOfRangeException("negative frequency not feasable");

        this.Frequency = frequency;
        this.Data      = data ?? throw new ArgumentNullException(nameof(data));
    }
}

internal class DAQmxMeasureDeviceAdapter : IMeasureDevice
{
    public Task WaitForHardwareReadyAsync()
        => Task.Factory.FromAsync(DAQmx.BeginWaitForHardware(), DAQmx.EndWaitForHardware);

    public Task StartSinusOutputAsync(double magnitude, double frequency)
        => Task.Factory.FromAsync(DAQmx.BeginSendSignal(magnitude, frequency), DAQmx.EndSendSignal);

    public Task<double[,]> ReadSequenceAsync(int sampelRate, int sampleCount)
        => Task.Factory.FromAsync(DAQmx.BeginReadSequence(sampelRate, sampleCount), DAQmx.EndReadSequence);

    public Task StopOutputAsync()
        => Task.Factory.FromAsync(DAQmx.BeginStopOutput(), DAQmx.EndStopOutput);
}

Im Adapter DAQmxMeasureDeviceAdapter hab ich die Methoden des fiktiven Typs DAQmx nur angenommen. Diese sind durch die tatsächlichen Methoden zu ersetzen -- das schafftst du sicher.

Die Verwendung wäre dann z.B.

C#-Code:
class Program
{
    static async Task Main(string[] args)
    {
        IMeasureDevice measureDevice                    = new DAQmxMeasureDeviceAdapter();
        var wobbler                                     = new Wobbler(measureDevice);
        var progress                                    = new Progress<FrequencyStepResult>(OnProgress);
        IReadOnlyCollection<FrequencyStepResult> result = await wobbler.SweepAsync(10, GetFrequencySteps(), progress);
    }

    private static void OnProgress(FrequencyStepResult frequencyStepResult)
    {
        // Use result
    }

    private static IEnumerable<double> GetFrequencySteps()
    {
        double currentFrequency = 10;

        for (int i = 1; i < 10; ++i)
        {
            yield return currentFrequency;
            currentFrequency *= 10;
        }
    }
}

Als Nebenbemerkung:
double[,] wenn du diesen  rectangular array vermeiden kannst wäre es noch besser, denn diese sind sehr unperformant, da die CLR Methoden-Aufrufe für die Zugriffe verwenden muss.  Jagged arrays werden von der CLR direkt unterstützt in Form von sz-Arrays und dort sind die Zugriffe direkte Speicherreferenzen.
Od. statt des Arrays kann auch eine eigenen Struktur verwendet werden, die dann in eine passende Collection gepackt wird.


mfG Gü
06.11.2019 10:57 Beiträge des Benutzers | zu Buddylist hinzufügen
ill_son ill_son ist männlich
myCSharp.de-Mitglied

Dabei seit: 03.09.2009
Beiträge: 174
Entwicklungsumgebung: Visual Studio 2013
Herkunft: Leipzig

Themenstarter Thema begonnen von ill_son

ill_son ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo gfoidl,

vielen Dank für deine Mühe. Damit, denke ich, sollte das Thema gelöst sein. Zu den Arrays, ich hab schon gemerkt, dass die nicht so "handy" sind. Leider liefern die Treiberfunktionen die Daten in diesem Format. Ich hab mir aber was gebastelt/recherchiert, dass mit Hilfe von LINQ praktische Listen daraus macht.

Falls jemand mal in die Verlegenheit kommen sollte:

C#-Code:
private double[] GetColumn(double[,] array, int columnNumber)
{
    return Enumerable.Range(0, array.GetLength(0)).Select(x => array[x, columnNumber]).ToArray();
}

public double[] GetRow(double[,] array, int rowNumber)
{
    return Enumerable.Range(0, array.GetLength(1)).Select(x => array[rowNumber, x]).ToArray();
}

Grüße und danke nochmals,

Alex

Dieser Beitrag wurde 2 mal editiert, zum letzten Mal von ill_son am 06.11.2019 14:11.

06.11.2019 14:10 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 15.11.2019 04:01