Laden...
Avatar #avatar-2894.jpg
gfoidl myCSharp.de - Team
Entwickler in der Gasmotorenbranche (nicht nur Software) Waidring Dabei seit 07.06.2009 6.911 Beiträge
Benutzerbeschreibung
FIS-Speedski (S-DH) world champion 2015 3x FIS-Speedski (S-DH) overall worldcup winner, 2008/09, 2009/10, 2012/13 5x Subaru Velocity Challenge winner, 2009, 2010, 2013, 2014, 2015 7x PopKL Verbier winner, 2009 2x, 2010 2x, 2011 2x, 2013 1x 27 victories in 69 international Speedski-races v_max = 208,333 km/h http://de.wikipedia.org/wiki/Günther_Foidl

Forenbeiträge von gfoidl Ingesamt 6.911 Beiträge

12.09.2019 - 13:24 Uhr

Hallo hypersurf,

schau auch in den Debug | Options | Debugging ob etwas aktiviert ist, das nicht unbedingt (jedesmal) benötigt wird. Z.B.* Debug-Symbole -- müssen heruntergeladen werden, falls nicht vorhanden

  • Enable Source Link Support -- Code-Dateien werden heruntergeladen, falls nicht existent
  • Enable source server support -- gleich wie oben
  • während des Debuggen "Show threads in source" bremst auch

Diese Punkte kannst du i.d.R. deaktivieren, außer sie werden beim Debuggen tatsächlich benötigt.

mfG Gü

11.09.2019 - 22:27 Uhr

Hallo Charly,

bei jedem Anhängen von weiteren Daten immer neu alloziert

siehe [FAQ] Besonderheiten der String-Klasse (immutabler Referenztyp mit Wertsemantik)

Kann man für die String Variable nicht gleich vorbestimmen wieviel Speicher sie von vornherein allozieren soll ?

Direkt: Ab .NET Core 2.1 mittels string.Create. Achtung: die Länge muss genau angegeben werden, da so direkt in den Speicher vom (dann) fertigen String geschrieben wird.

Indirekt: via StringBuilder wie hier schon vorgeschlagen.

Alloziert man selbst Speicher und schreibt darin mit einem Pointer weg ? (unsafe) der Bedarf könnte vorab Bytegenau berechnet werden - wäre wohl das schnellste denke ich ?

Nicht nötig, umständlich und fehleranfällig.

Memorystream erzeugen - kann man da eine Speichergröße vorallozieren um nicht unsafe selbst Speicher reservieren zu müssen ?

Ja, siehe :rtfm:

Wenn er einfach nur "hinten dran" Speicher neu allokiert dürfte das ja kein Problem sein.

Im StringBuilder gibt es einen Buffer für die Zeichen des zu erstellenden Strings. Wenn der Buffer durch die Appends voll wird, so wird ein doppelt so großer Buffer* erstellt, der Inhalt des bisherrigen Buffers in den neuen Buffer kopiert und der bisherige Buffer verworfen (bis in der GC abräumt).
Daher auch der Hinweis von Th69: Anfängsgröße bekanntgeben, das spart Buffer-Wachstum wie vorhin beschrieben.

* kann sein dass sich das geändert hat und eine andere Wachstumsrate verwendet wird, das ändert aber nichts an der Aussage

Es spricht aber nichts dagegen, die Daten auch gleich auf Platte zu schreiben

👍
Das sollte auch gemacht werden, denn es bringt nichts zuerst im RAM einen großen String zusammenzubauen, der dann fürs Schreiben in die Datei kodiert (z.B. UTF-8) werden muss um dann in den Datei-Buffer zu schreiben, ...
Das kann auch direkt geschrieben werden, z.B. mittels StreamWriter der auch Methoden fürs formatierte Schreiben bietet.

Dann gibt es auch keinen Riesenstring der vermutlich im LOH (large object heap) landet (falls er mehr als 85e3 bytes hat) und erst mit einer Gen2-Collection des GC abgeräumt wird -- also sehr selten.
Beim direkten Schreiben können zwar mehrere Gen0-Collections vom GC auftreten, aber die sind nicht schlimm.

Dann dürftest du auch die performanteste Umsetzung haben.
Das wegschreiben, kannst du dann auch nicht mehr optimieren.

Ich fürchte das geht jetzt zuweit, aber der Vollständigkeithalber:
Vermutlich geht es mit IO.Pipelines noch performanter und v.a. mit weniger Allokationen.
ABER: der Code wird dadurch aufwändiger und umständlicher (= weniger wartbar). Sollte es keine Server-Lösung od. keine zwingende Forderung nach schnellstem Code geben, so soll wartbarer Code bevorzugt werden und keine Suche nach ultimativer Performance gestartet werden.

Ich würde mit dem StreamWriter direkt schreiben. Ist einfach umzusetzen, performant und der Code leserlich.

mfG Gü

08.09.2019 - 19:19 Uhr

Hallo RafaelVogt,

lokale Funktionen wurden mit C# 7 eingeführt. Durch praktischen Einsatz dieser Sprachversion wurde bemerkt, dass oft versehentliche lokale Variablen und Argumente in der lokalen Funktion verwendet wurde, worauf der Compiler dafür eine "Closure" erstellen musste. Da dies eben oft unbewusst gemacht wurde, entschied man sich mit C# 8 dem Abhilfe zu leisten und somit die "statischen lokalen Funktionen" einzuführen -- gekennzeichnet mit dem static Schlüsselwort bei der Deklaration der lokalen Funktion.

Wie bereits erwähnt und auch in der Fehlermeldung (indirekt) angegeben, muss dafür die Sprachversion 8.0 gesetzt werden. latest genügt hier nicht, es muss explizit 8.0 od. preview verwendet werden.

mfG Gü

06.09.2019 - 16:11 Uhr

Hallo RafaelVogt,

Das ist doch total simpel 😄
Mit ...

  
short value20 = checked(Convert.ToInt16(Console.ReadLine()));  
  

... machst du genau was?

Sorry... es ist echt verwirrend.

Hier macht das checked nicht viel Sinn, da Int16 der Typ des Schlüsselworts short ist. Da kannst du es weglassen.

Anders wäre es z.B. bei


short s0 = checked(Convert.ToUInt16(Console.ReadLine()));

da bei der Konvertierung* ein ushort zurückgegeben wird, das könnte bei der Zuweisung zu short überlaufen.
Od. beim Beispiel von oben mit der Zuweisung zu byte.

Generell ist auf (arithmetischem) Über-/Unterlauf zu achten, wenn verschiedene Wertebereiche im Spiel sind. Entweder wird explizit im Code geprüft ob es passt (z.B. per if) od. mit checked od. gar nicht, falls Überlauf akzeptabel ist.

* bitte unbedingt meinen obigen Hinweis zu den TryParse-Methoden beachten.

mfG Gü

06.09.2019 - 10:58 Uhr

Hallo RafaelVogt,

anstatt der checked Blöcke kann das auch "global" eingestellt werden.
Projekteigenschaften -> Build -> Advanced -> Checkbox "Check for arithmetic overflow/underflow"
Wobei ich jedoch die expliziten checked Blöcke und Anweisungen bevorzugen, da es eben sofort ersichtlich ist worum es geht.

Siehe zum Thema auch CA2233: Operations should not overflow

Da du gerade beim Lernen bist, noch ein Hinweis:

  
short value20 = Convert.ToInt16(Console.ReadLine());  
  

erzeugt einen Laufzeitfehler, falls Console.ReadLine keine Zahl (in Text-Form) liefert. Wenn du z.B. "abc" eingegeben hast anstatt einer erwarteten Zahl, so stürzt das Programm ab.
Abhilfe schafft entweder den Fehler mittels try catch zu fangen und behandeln od. noch besser


if (short.TryParse(Console.ReadLine(), out short value20))
{
    // Zahl konnte erfolgreich geparst werden
}
else
{
    // Zahl konnte nicht geparst werden
}

zu verwenden. Ob und wie auf das "nicht parsen" reagiert wird, hängt von der Anwendung ab.

mfG Gü

29.08.2019 - 18:56 Uhr

Hallo grubewol,

ein doppelter wert

Meinst du damit explizit zwei hinteranderfolgende gleiche Zeichen?

Das könnte ganz naiv so gelöst werden:


using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            Dictionary<string, int> counts = GetDoubledCharsCount("aabndmggmvndaasdfsdf");

            Console.WriteLine("Group\tCount");
            foreach (var kvp in counts.OrderByDescending(k => k.Value))
            {
                Console.WriteLine($"{kvp.Key}\t{kvp.Value}");
            }
        }

        private static Dictionary<string, int> GetDoubledCharsCount(string text)
        {
            var counts = new Dictionary<string, int>();

            for (int i = 1; i < text.Length; ++i)
            {
                if (text[i] == text[i - 1])
                {
                    string key = text.Substring(i - 1, 2);

                    if (counts.TryGetValue(key, out int count))
                    {
                        counts[key] = count + 1;
                    }
                    else
                    {
                        counts.Add(key, 1);
                    }
                }
            }

            return counts;
        }
    }
}

Die Aufgabe verallgemeinert auf beliebige Längen --> Longest common subsequence problem

mfG Gü

29.08.2019 - 12:55 Uhr

Hallo tobi45f,

Fehlt noch was?

Ich kenn deinen Code nicht, daher schwer genau zu sagen was es ist.
Bedenke jedoch dass der Code bis zum ersten await synchron ausgeführt wird und dann hängt es davon abder erwartete Task schon fertig ist, d.h. es wird synchron weitergearbeitet der erwartete Task nicht fertig ist und im Hintergrund (asynchron) ausgeführt wird

Du kannst versuchsweise ziemlich am Beginn der Methode ein await Task.Yield() einbauen um async-Ausführung zu "erzwingen" und schauen ob es besser wird.

* der Einfachheithalber schreib ich nur Task, korrekter wäre "Awaitable" zu dem neben Task, ValueTask auch jedes selbst implementierte Awaitable gehört.

mfG Gü

27.08.2019 - 18:23 Uhr

Hallo tobi45f,

richtig? Vermutlich muss ich das Datenbank lesen+schreiben noch asyncen?!

Ja, alles async -- async ist viral 😃

mfG Gü

23.08.2019 - 15:37 Uhr

Hallo tobi45f,

Den GetAwaiter-Code verstehe ich leider gar nicht

Ganz grob: wenn der C#-Compiler ein await sieht, so verlangt er nach einem Typen der eine GetAwaiter-Methode (auch als Erweiterungsmethode) hat und diese Methode muss ein "awaitable" zurückgeben. Das ist z.B. bei Task der Fall.
Hier haben wir einen Process und wollen diesen asynchron erwarten, da der C#-Compiler jedoch nicht weiß was und wie er dies tun soll, helfen wir ihm mit dieser Erweiterungsmethode.

dass ich das nicht so umsetzen kann, da ich den Konsoleninhalt auslese, um das Simulationsergebnis zu interpretieren.

Ja so, kann die Erweiterungsmethode nicht 1:1 verwendet werden, da nur der ExitCode zurückgegeben wird und nicht die Ausgabe aus stdout. Dazu muss diese aus meinem vorigen Post angepasst werden:


using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            string stdout = await "cmd.exe".ReadStdOut();
            Console.WriteLine($"stdout: {stdout}");
        }
    }

    internal static class ProcessExtensions
    {
        public static Process ReadStdOut(this string command)
        {
            var startInfo = new ProcessStartInfo(command);
            startInfo.RedirectStandardOutput = true;
            startInfo.UseShellExecute = false;
            startInfo.CreateNoWindow = true;

            var process = new Process();
            process.StartInfo = startInfo;
            process.Start();

            return process;
        }

        public static TaskAwaiter<string> GetAwaiter(this Process process)
        {
            var tcs = new TaskCompletionSource<string>();
            process.EnableRaisingEvents = true;

            process.Exited += (s, e) => tcs.TrySetResult(process.StandardOutput.ReadToEnd());
            if (process.HasExited) tcs.TrySetResult(process.StandardOutput.ReadToEnd());

            tcs.Task.ContinueWith(
                (_, p) => (p as Process).Dispose(),
                process,
                TaskContinuationOptions.ExecuteSynchronously);

            return tcs.Task.GetAwaiter();
        }
    }
}

(als Beispiel, kannst und sollst du natürlich anpassen wie es dir passt).

aus dem await Task<TradeSolutions>.Factory.StartNew(() rausgezogen, damit nicht der API-Kram nicht den Task beendet.

Das hatten wir deshalb gemacht, damit ein Fehler im "inneren Task" auch beobachtet werden kann. Durch Task.Factory.StartNew war das nicht der Fall, daher ist die App gecrasht.
Wenn hier nur der Process per await erwartet wird, so gibt es nur einen Task und hat dieser einen Fehler, so wird dieser auch gefangen. Kein Problem.
Allerdings ist in obiger (meiner) Umsetzung vom GetAwaiter für den Process nichts eingebaut um den Task in eine Fehler-Zustand zu setzen (ginge via tcs.TrySetException, das kannst du ja selbst machen -- siehe angehängtes Beispiel)

mfG Gü

21.08.2019 - 12:29 Uhr

Hallo tobi45f,

ein Berechnungsalgorithmus bestehende aus einer Schleife mit: Datenbanken lesen, Werte Interpretieren, ...

Somit kannst du beim DB-Lesen asynchron vorgehen.

Auch den externen Prozess kannst du asynchron "erwarten". Dazu brauchst du nur eine Erweiterungsmethode, siehe Bsp.:


using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading.Tasks;

namespace ConsoleApp1
{
    class Program
    {
        static async Task Main(string[] args)
        {
            int exitCode = await Process.Start("cmd.exe");
            Console.WriteLine($"exit code: {exitCode}");
        }
    }

    internal static class ProcessExtensions
    {
        public static TaskAwaiter<int> GetAwaiter(this Process process)
        {
            var tcs = new TaskCompletionSource<int>();
            process.EnableRaisingEvents = true;

            process.Exited += (s, e) => tcs.TrySetResult(process.ExitCode);
            if (process.HasExited) tcs.TrySetResult(process.ExitCode);

            tcs.Task.ContinueWith(
                (_, p) => (p as Process).Dispose(),
                process,
                TaskContinuationOptions.ExecuteSynchronously);

            return tcs.Task.GetAwaiter();
        }
    }
}

etzt ist der Code zwar nicht mehr so Chronologisch übersichtlich im Ablauf,

Das lässt sich aber wieder übersichtlicher gestalten, ev. durch gezieltes Refactoring.
Leserlicher Code ist sehr wichtig und auch dass sinnvolle Bezeichner verwendet werden.

mfG Gü

20.08.2019 - 16:32 Uhr

Hallo tobi45f,

wann ich über einen neuen Task die Methode aufrufe und wann ich nur die asynchrone Methode mit await aufrufe? Wonach kann man sich da richten, wie/wann man was anwendet?

Grundsätzlich muss bei Vorgängen unterschieden werden ob diese CPU- od. IO-lastig sind.
CPU-lastig sind z.B. Berechnungen die direkt im RAM erfolgen können.
IO ist Datei-, Netzwerk-, Datenbank-Zugriff usw.

Task ist dabei eine .NET-Abstraktion von "Arbeitsaufträgen", welche nicht zwingend mit Thread gleichzusetzen sind.

Der Großteil von IO kann in .NET asynchron erfolgen und wird (mittlerweile) durch Tasks abstrahiert. Z.B.


public async Task<int> DownloadAsync(string url)
{
    // Code vorher
    string result = await _httpClient.GetStringAsync(url);
    // Code nachher
    return result.Length;
}

Was passiert hier?1.die Methode DownloadAsync wird synchron aufgerufen 1.Code vorher wird synchron ausgeführt 1.Code GetStringAsync wird im HttpClient synchron ausgeführt, bis intern ein IO-async-Vorgang gestartet wird (der HTTP-Request), dann wird von GetStringAsync ein Task<string> zurückgegeben 1.durch das await (und dem vom C#-Compiler umgeschrieben Code) wird das Ergebnis vom Task<string> erwartet (nota bene: ich schreibe bewusst nicht "gewartet", denn diese würde blockierendes Warten implizieren und das gilt es nämlich zu vermeiden, daher "erwartet") 1.DownloadAsync gibt dem Aufrufer der Methode ein Task<int>-Objekt zurück 1.sobald der Http-Request fertig ist und der Http-Response zurückkommt, so wird vom IO-ThreadPool ein sogenannter "IO-Abschlussthread" genommen, welcher die weitere Verarbeitung durchfüht --> jetzt wird mit dem Code nach dem await (der Continuation) forgefahren 1.Code nachher wird wieder syncrhon ausgeführt (aber möglicherweise in einem anderen Therad als Code vorher*) 1.es wird das Ergebnis return result.Length zurückgeben, dabei der Task<int> "vervollständigt" und dessen Result-Eigenschaft hat nun den Wert der per return zurückgegeben wird od. die Exception-Eigenschaft hat einen Fehler im Fehlerfall

* dieses Verhalten kann auch mit ConfigureAwait beeinflusst werden -- behandle ich hier nicht weiter

Der Vorteil von async-IO ist, dass keine CPU-Threads v.a. aus dem ThreadPool verbraten werden od. keine solche Threads unnötig durch Warten blockiert werden -- dies ist v.a. in Server-Umgebungen für die Skalierbarkeit entscheidend.

Daher sollte IO so gut es möglich ist asynchron erfolgen.
In ASP.NET Core sind standardmäßig z.B. keine synchronen (= blockierenden) IO-Vorgänge gestattet (zu Recht 😉).
Aber auch für Dektop-Anwendungen ist dies vorteilhaft, da das gesamte System einfach besser läuft, wenn nicht (wertvolle) CPU-Thread wartend ihre Zeit verbringen.

Bei reiner CPU-Last (Berechnungen, etc.) ist zwischen Server- und Desktop-Anwendungen zu unterscheiden.

Server-Anwendungen können die Berechnungen durchwegs in ihrem Request-Thread durchführen (nicht jedoch IO-Vorgänge, siehe vorhin). Da der Request-Thread bereits dem ThreadPool entnommen wurde, macht es kaum Sinn nochmals in einen anderen ThreadPool-Thread die Arbeit zu delegieren.

Desktop-Anwendungen müssen wegen [FAQ] Warum blockiert mein GUI? CPU-lastige Aufgaben in einem nicht GUI-Thread ausführen, d.h. die Arbeit in einem anderen Thread auslagern.
Wenn es sich um "kurze" Dauern für die CPU-Aufgabe handelt ist der ThreadPool zu bevorzugen, da das Erstellen und Zerstören eines Threads recht aufwändig ist (daher gibt es ja den ThreadPool). Dauer die CPU-Aufgabe zu lange, so wird zwar der ThreadPool durch seine Thread-Injektion-Heuristik mehr Thread dem Pool hinzufügen, aber optimal arbeitet der ThreadPool nur für kurze Aufgaben.
Kurz und lang sind dabei relativ, aber so als groben Richtwert kann man 1s verwenden.
Der Einfachheithalber würde ich aber für Berechnungen auch den ThreadPool verwenden, da der Code einfach kürzer und eleganter wird.

Also um die oben zitierte Frage auch kurz zu beantworten:* für IO "immer" per async / await ohne Task.Factory.StartNew bzw. ohne Task.Run

  • für CPU bei Desktop mittels Task.Run (od. entsprechende ThreadPool.QueueUserWorkItem-Äquivalente)

die Routine, die dem Post vorweg geht. Diese hat eine lange Berechnungsdauer (einige Minuten)

OK, das wusste ich vorher noch nicht. Es geht dabei um GetMarketArea? Ist das wirklich eine Berechnung im Sinne von CPU-Last od. doch eher Datenbank-Zugriffe, usw. also IO?

Nehmen wir an es ist CPU, dann ist auch das möglich. Beispiel:


#define CPU_BOUND

using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace WpfApp1
{
    public partial class MainWindow : Window
    {
        private readonly Worker _worker = new Worker();
        private CancellationTokenSource _cancellationTokenSource;

        public MainWindow()
        {
            InitializeComponent();
        }

        private async void StartButton_Click(object sender, RoutedEventArgs e)
        {
            if (_cancellationTokenSource == null)
                _cancellationTokenSource = new CancellationTokenSource();

            startButton.IsEnabled = false;
            stopButton.IsEnabled = true;

            try
            {
                statusTextBox.Text = "MarketArea...";
#if CPU_BOUND
                MarketArea marketArea = await Task.Run(() => _worker.ComputeMarketArea(), _cancellationTokenSource.Token);
                if (_cancellationTokenSource?.IsCancellationRequested ?? true) return;
#else
                MarketArea marketArea = await _worker.GetMarektAreaAsync(_cancellationTokenSource.Token);
#endif
                statusTextBox.Text = "MarketArea OK";

                if (await _worker.PostObjectAsync("bla bla", marketArea, _cancellationTokenSource.Token))
                {
                    statusTextBox.Text = "OK";
                }
                else
                {
                    // Nicht Erfolg behandeln (od. PostObjectAsync gibt einen string / Exception zurück, dazu müsste ich
                    // aber das Problem / Aufgabe besser verstehen)
                }
            }
            catch (OperationCanceledException)
            {
                statusTextBox.Text = "Übermittlung wurde abgebrochen";
            }
            catch (Exception ex)
            {
                statusTextBox.Text = ex.Message;
            }
            finally
            {
                this.Reset();
            }
        }

        private void StopButton_Click(object sender, RoutedEventArgs e) => this.Reset();

        private void Reset()
        {
            _cancellationTokenSource?.Dispose();
            _cancellationTokenSource = null;

            this.ResetButtons();
            statusTextBox.Text = string.Empty;
        }

        private void ResetButtons()
        {
            startButton.IsEnabled = true;
            stopButton.IsEnabled = false;
        }
    }

    public class MarketArea { }

    public class Worker
    {
        private readonly HttpClient _httpClient = new HttpClient();

        public async Task<MarketArea> GetMarektAreaAsync(CancellationToken cancellationToken = default)
        {
            // Lange Operation simulieren
            await Task.Delay(500, cancellationToken).ConfigureAwait(false);

            return new MarketArea();
        }

        public MarketArea ComputeMarketArea()
        {
            // Lange Berechnung simulieren
            for (int i = 0; i < int.MaxValue; ++i)
            {
            }

            return new MarketArea();
        }

        public async Task<bool> PostObjectAsync<T>(string path, T data, CancellationToken cancellationToken = default)
        {
            // HttpClient-header bereits gesetzt

            try
            {
                // Wenn hier oft der Server nicht verfügbar ist, so kann ein Retry, etc. eingebaut werden
                // Siehe z.B. Polly (einfach googlen im Kontext)
                HttpResponseMessage response = await _httpClient.PostAsJsonAsync(path, data, cancellationToken).ConfigureAwait(false);

                if ((int)response.StatusCode == 450)
                {
                    // Logging -- kein Grund eine Exception zu schmeißen, wenn diese ein paar Zeilen später im catch
                    // gefangen wird, das kann dann gleich hier erfolgen
                    string msg = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    // msg verarbeiten

                    return false;
                }

                return response.IsSuccessStatusCode;
            }
            catch (Exception ex)
            {
                // Logging, etc. von ex

                // Es geht hier nur um den Fehlerfall, daher den Fehler erneut schmeißen
                // Nicht HTTP-200 werden hier nicht behandelt
                throw;
            }
        }
    }
}

Es gibt so viele Möglichkeiten das umzusetzen. Z.B. in ComputeMarketArea könnte auch der CancellationToken übergeben werden um dort kooperativ abzubrechen.

Aber letztlich reicht ein await Task.Run aus um die CPU-Aufgabe zum ThreadPool zu verlagern, ohne dabei irgendwelche Fehler nicht behandeln zu können. Probiers einmal aus.

Bzgl. des httpClient meinst du das hier, zwar nicht HttpClientFactory aber inhaltlich sehr ähnlich?

Genau 😃

mfG Gü

20.08.2019 - 11:35 Uhr

Hallo tobi45f,

ersetzte


await Task.Factory.StartNew(() => berechnung.PostTAsync(pathFidList, berechnung.GetMarketArea()), token, TaskCreationOptions.LongRunning, TaskScheduler.Default);

durch


await berechnung.PostTAsync(pathFidList, berechnung.GetMarketArea()), token);

(den CancellationToken sollte als Argument an PostTAsync übergeben werden können)

Dann sollte das Fehlerhandling korrekt klappen.
Bei deinem Code mit Task.Factory.StartNew wird nicht nur unnötig ein Thread gestartet, sondern auch await "erwartet" den Task der gerade gestartet wurde und nicht jenen der vom PostAsJsonAsync stammt. Somit resultiert das in einen "unbehandleten Task-Fehler" und wegen dem finally wird dort fortgesetzt.

Ohne das Task.Factory.StartNew wird bei await das "Ergebnis" von PostAsJsonAsync erwartet und das kann ein Resultat (hier das bool) od. eben ein Fehler sein.

Da PostAsJsonAsync für sich schon IO-asynchron ist, macht es -- wie vorhin schon kurz erwähnt -- keinen Sinn extra einen Thread zu starten (wegen LongRunning wird kein ThreadPool-Thread verwendet).

Weiters solltest du den HttpClient (für die gleiche Url) nicht jedesmal neu erstellen, da selbst beim Dispose die Socket-Verbindungen nicht ganz geschlossen werden und es somit zu einer "Ausschöpfung" kommen kann. Google mal danach, dieser Punkt wird ausführlich behandelt, daher gebe ich hier nicht alles wieder.
Kurz: verwende am besten die HttpClientFactory, denn diese verwendet einen Pool und erledigt das alles für dich.

mfG Gü

16.08.2019 - 10:59 Uhr

Hallo dila71,

Ich dachte bisher Double Enums gäbe es nicht.
Google sagt auch nur, geht nicht.
Nach was kann/muss ich googeln, um mich darüber zu informieren.

Naja, die offizielle Doku?

Zitat von: enum (C# Reference)
Every enumeration type has an underlying type, which can be any
>
. double ist kein "integral numeric type" (siehe dazu auch [FAQ] Double und Float: Fehler beim Vergleich und Rundungsfehler).

mfG Gü

12.08.2019 - 19:09 Uhr

Hallo CrocodileDundee,

.NET Core 3 wird auch direkt auf dem Raspberry laufen.

.NET Core 2.1 und neuer laufen auf dem rasperrybi (3) problemlos.

eine selbst geschriebene dll

Ergänzend zu Abts Hinweis "funktioniert nicht": es ist eh eine managed DLL? Bei einer nativen DLL muss diese für arm (rpi) kompiliert worden sein, sonst kann es ohnehin nicht gehen.

mfG Gü

09.08.2019 - 17:34 Uhr

Hallo FrankenDerStein,

ich will Abts Antwort ergänzen / untermauern.

(Named) Pipes sind genauso wie TCP "low level" und i.d.R. ist es nicht nötig so tief (im Stack) selbst zu programmieren. Wesentlich effizienter und komfortabler ist es Frameworks zu verwenden, welche die low level Kommunikation wegabstrahieren. gRPC ist momentan das Maß der Dinge (aktuell auch zurecht).

Pipes, TCP, etc. würde ich nur dass verwenden wenn es nicht anders geht.

mfG Gü

25.07.2019 - 18:57 Uhr

Hallo CoderboyPB,

(Wo nur Text gesendet wird) dann 'Pseudo Async' machen Task.FromResult(TResult) Method hilft dir hierbei.


public Task<string> FooAsync()
{
    string result = // ...

    return Task.FromResult(result);
}

mfG Gü

22.06.2019 - 10:58 Uhr

Wegen [Hinweis] Wie poste ich richtig? Punkt 2.2 ==> geschlossen

Crosspost

21.06.2019 - 12:17 Uhr

Hallo Medicus,

Gibt es dafür auch eine Verbindung über ADO.NET? Google-Suche nach ado.net as400 😉

Sollte ich den Code für die Anmeldung im 'program.cs' oder Hauptform schreiben?

Weder noch, siehe dazu [Artikel] Drei-Schichten-Architektur

mfG Gü

20.06.2019 - 18:36 Uhr

Hallo,

ja, es gibt sehr wohl eine Middleware/Pipeline, wie die Konfiguration verarbeitet wird.

wenn wir unter Middleware die ASP.NET Core Middleware verstehen -- davon ist im Kontext hier auszugehen -- so hat die Konfiguration damit nichts zu tun.

Configuration Builder.
Jede gefundene, gematche Konfiguration durchläuft dann eine Pipe.

Mag sein, aber nicht in ASP.NET Core, wie im Code zu sehen ist 😉
Im ConfigurationBuilder ist es im Wesentlichen eine List<IConfigurationSource> zu der Einträge hinzugefügt werden um dann im Build (gem. Erbauer (Entwurfsmuster), daher auch der Suffix im Namen) die ConfigurationRoot zu erstellen. Dabei spielt eben die Liste eine entscheidende Rolle, da so über die Reihenfolge des Hinzufügens die resultierenden Config-Werte ermittelt werden (kurz: das später Hinzugefügte überschreibt das vorherige). Mit Pipe hat das nichts zu tun, da es eine (triviale) Iteration vom Listenende zum Listenanfang ist.

mfG Gü

19.06.2019 - 16:24 Uhr

Hallo,

Konfigurationsmiddleware

Die gibt es nicht (zumindest per Standard ASP.NET Core). ConfigurationBuilder hingegeben schon.

mfG Gü

17.06.2019 - 12:01 Uhr

Hallo deluxe13,

willst du so etwas?


using System;
using System.Collections.Generic;
using NUnit.Framework;

namespace Tests
{
    [TestFixture]
    public class Tests
    {
        [Test, TestCaseSource(nameof(Correct_rounding_TestCases))]
        public TimeSpan Correct_rounding(TimeSpan input)
        {
            return input.RoundToWholeDays();
        }

        private static IEnumerable<TestCaseData> Correct_rounding_TestCases()
        {
            yield return new TestCaseData(new TimeSpan(1, 0, 0, 0)).Returns(new TimeSpan(1, 0, 0, 0));
            yield return new TestCaseData(new TimeSpan(1, 0, 0, 1)).Returns(new TimeSpan(2, 0, 0, 0));
            yield return new TestCaseData(new TimeSpan(1, 0, 1, 0)).Returns(new TimeSpan(2, 0, 0, 0));
            yield return new TestCaseData(new TimeSpan(1, 1, 0, 0)).Returns(new TimeSpan(2, 0, 0, 0));
        }
    }

    public static class Extensions
    {
        public static TimeSpan RoundToWholeDays(this TimeSpan timeSpan)
        {
            int days = timeSpan.Days;

            return timeSpan.TotalDays == days
                ? timeSpan
                : new TimeSpan(days + 1, 0, 0, 0);
        }
    }
}

Hier hab TotalDays verwendet.

mfG Gü

17.06.2019 - 11:51 Uhr

Hallo RoyalRoy,

angenommen es gibt es Möglichkeit die Verbindung zu prüfen.

Folgendes Szenario:1.Verbindung wird hergestellt 1.Verbindung wird überprüft 1.Daten werden auf den Server geschrieben

Das willst du haben, damit ein Abbrechen nach dem Herstellen der Verbindung und vor dem Schreiben erkannt wird. Korrekt?

Stell dir jetzt vor, dass die Verbindung nach dem Prüfen abgebrochen wird. Was hast du dann gewonnen?

Kurz: du baust mehr Aufwand in deine Anwendung ein ohne dabei einen Netto-Gewinn zu haben.

Ich würde im Fehlerfall einfach den Fehler / Exception handhaben (Retry, Loggen, etc. wie es halt angebracht ist) und im Regelfall davon ausgehen dass es eh klappt.

mfG Gü

17.06.2019 - 11:46 Uhr

Hallo deluxe13,

schau dir TimeSpan.TotalMinutes dazu an. Den Rest schaffst du dann schon 😉

mfG Gü

15.06.2019 - 17:13 Uhr

Hallo deluxe13,

Was mache ich falsch?

Dass du dir hier eine Antwort erhoffst, ohne uns etwas über deinen Code zu sagen / zu zeigen 😉

Wir haben keine Ahnung wie deine Anwendung aufgebaut ist und was sie machen soll, daher können wir entweder nur pauschale Antworten geben, die werden dir aber nicht viel weiterhelfen, od. raten und auch das wird dir nicht weiterhelfen.

Also stell dir vor du liest diese Frage ohne zu wissen worum es geht. Was würdest du antworten?

Als pauschale Antwort kann ich hier nur schreiben, dass es i.d.R. keine gute Idee ist beim Programmstart den DB-Kontext zu laden und dann für die dauer der Anwendung offen zu halten, siehe Unit of Work-Pattern.

mfG Gü

05.06.2019 - 13:23 Uhr

Hallo Anna85,

geht es um das selbe Problem wie bisher od. ist es ein Neues?

mfG Gü

04.06.2019 - 17:45 Uhr

Hallo BhaaL,

Kommt die NRE beim Zugriff auf person.Name.Length oder bereits beim Konstruktor-Aufruf new Person(null)?

Das läuft ab wie bisher, also beim Zugriff auf Length, da Name null ist.

Wies scheint hatte ich hier ein altes Proposal ...

Das soll in Zukunft auch kommen. Allerdings weiß ich nicht wie "Zukunft" hier definiert ist.

mfG Gü

04.06.2019 - 13:09 Uhr

Hallo p!lle,

konkret geht es um die Analogie zu Nullable types (wie z.B. int?), daher passt der Name mit Nullable Reference Types schon.

Mit #nullable enable werden die Nullable Reference Types als optionale Erweiterung der C#-Sprache aktiviert.

Optional ist das Ganze deshalb, damit für betehenden Code keine breaking-change passiert bzw. bestehender Code auch weiterhin kompiliert ohne Warnungen / Fehler zu generieren.

mfG Gü

03.06.2019 - 21:35 Uhr

Hallo zusammen,

wie vllt. bekannt ist wird mit C# 8.0 der Kampf dem Billion Dollar MistakeTony Hoare) angesagt. Siehe dazu Design with nullable reference types.

Allerdings darf man sich v.a. bei öffentlichen (public) APIs nicht in falscher Sicherheit wiegen.

Diese Gefahr / Tücke will ich mit nachfolgenden simplen Beispiel demonstrieren.


#nullable enable

    public class Person
    {
        public string Name { get; }

        public Person(string name) => this.Name = name;
    }
}

Für die öffentliche Klasse Person wurde das "nullable feature" explizit aktiviert*, somit könnte man meinen, dass name eben nicht null sein darf.
Das ist für diese Klasse auch korrekt**, andernfalls hätte der Parameter als string? name deklariert werden müssen um null zu erlauben.

Wenn nun ein Benutzer unserer Klasse Person Nullable nicht aktiviert hat, wie im folgenden Code


    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person(null);
            Console.WriteLine(person.Name.Length);
        }
    }

, so kompiliert das Programm ohne Fehler / Warnung, zur Laufzeit gibt es aber eine NullReferenceException, da nirgends validiert wurde ob das Konstruktor-Argument name ungleich Null ist.

Wäre für obigen Code Nullable auch aktiviert worden, so hätte der C#-Compiler eine Warnung / Fehler ausgegeben.
Beim Erstellen eines "public apis" können wir aber nicht davon ausgehen, dass jeder Benutzer Nullable aktiviert hat und somit können wir in die vorhin demonstrierte Falle tappen.

Somit will ich mit diesem Beitrag den Hinweis ausprechen:
Auch mit C# 8.0 Nullable müssen public api auf null validiert werden!.

D.h. eingangs erwähnte Person-Klasse ist auch in Zukunft korrekt zu schreiben:


#nullable enable

    public class Person
    {
        public string Name { get; }

        public Person(string? name) => this.Name = name ?? throw new ArgumentNullException(nameof(name));
    }

Hier wurde als Typ string? angegeben, damit verdeutlicht wird dass Verwender dieser Klasse auch null dürfen.

Wäre als Typ string angegeben und der Verwender dieser Klasse hat Nullable sowie TreatWarningsAsErrors aktiviert, so ließe sich der Code mit null gar nicht kompilieren. Dies kann u.U. ein Hindernis sein. Daher bevorzuge ich (momentan***) die Variante mit string?.

* es ist auch eine Aktivierung via Projekteinstellungen möglich, ich empfehle aber die Aktivierung per Compiler-Direktive vorzunehmen, da so beim Lesen vom Code sofort ersichtlich dass "opt-in" für Nullable durchgeführt wurde.

** korrekt im Rahmen der vom Compiler durchgeführten statischen Codeanalyse, die entweder als Warnung od. bei <TreatWarningsAsErrors>true</TreatWarningsAsErrors> (in der csproj) als Fehler ausgegeben wird

*** Nullable ist noch relativ neu, daher kann es sein dass sich meine Präferenz dafür ändert und string (also ohne das Zulassen von null) die bessere Wahl ist

mfG Gü

01.06.2019 - 20:52 Uhr

Hallo CoderboyPB,

es sind "normale" Umgebungsvariablen. Die Gültigkeit ist entweder auf Betriebssystemebene, od. die aktuelle Shell / Programm und deren Sub-Shells / aufgerufenen Programme.

ASP.NET Core hat hier keine Sonderstellung in Bezug auf die Umgebungsvariablen.

mfG Gü

30.05.2019 - 09:31 Uhr

Hallo MinisBett,

auf 8/10 Punkten. Mit Asynchronen Dingen, "await", Tasks, usw. habe ich noch nichts gemacht,

Dann kann 8/10 nicht stimmen 😉
Ich würde dann am besten ein Projekt suchen / erstellen um mit den asynchronen Dingen rumzuspielen. Genauso würde ich beim Framework auf .NET Core setzen, wenn du dort noch keine Erfahrung hast.

mfG Gü

29.05.2019 - 09:23 Uhr

Hallo Anna85,

wegen [Hinweis] Wie poste ich richtig? Punkt 2.2 ==> geschlossen

Crosspost.

mfG Gü

23.05.2019 - 09:55 Uhr

Hallo Xargus,

im K-Array stehen nur boolsche Werte?
Dann nimm ein Bitfeld / Bitvektor und setzt entsprechend Index das jeweilige Bit. So kannst du einfach mit == 0 od. != 0 prüfen.

Siehe dazu auch [Artikel] Bitoperationen in C#

mfG Gü

19.05.2019 - 12:21 Uhr

Hallo Olii,

schön dass du eine Lösung gefunden hast, und dein Thema dementsprechend.auch als [gelöst] markiert hast. Noch besser wäre es, wenn du eine Antwort erstellt hättest, damit der Thread nicht mehr in den "Fragen ohne Antwort" auftaucht. Durch diese Antwort ist das Ziel aber auch erreicht.

mfG Gü

15.05.2019 - 21:12 Uhr

Hallo T-Virus,

bei Parallel.For/Foreach nicht ohne die Einstellung für jede Aufgabe intern dann einen eigenen Task starten?

Korrekt. Daher gibt es -- neben den von dir genannten Möglichkeiten -- auch die "Partitioner", mit denen dann pro "Range" ein Task erstellt wird.

Wobei ganz korrekt "Task starten" nicht ist, denn der Task wird zur Ausführung per Scheduler standardmäßig an den ThreadPool übergeben. Gestartet wird dabei nichts.

Falls diese länger laufen, würde er ggf. neue Threads im ThreadPool anfordern, was bei großen Dateien dann deine Thread Anzahl immer weiter anwachsen lässt.

Die Thread-Injection-Logik kapiert das schon dass der "Durchsatz" nicht besser wird und nimmt ggf. sogar Threads wieder weg. Basierend auf einem Hillclimb-Algorithmus. Das Problem sehe ich nicht, sollte aber dennoch nicht ganz außer Acht gelassen werden, insofern ist dein Hinweis gut und passend.

Hallo Taipi88,

ich würde das Problem eher darin sehen, dass paralellisiert und "gleichzeitig" auf das Dateisystem zugegriffen wird. D.h. wenn das Dateisystem dafür nicht ausgelegt ist, so handelst du dir hier einen Flaschenhals ein.
Andererseits fällt augrund der Größe der Dateien ein Producer-Consumer-mäßiges sequentielles Laden der Dateien in den RAM und dann parallele Consumer-Verarbeitung womöglich auch aus.

Es hängt also vom Dateisystem, dessen Konfiguration, vom RAM, etc. ab.

Die meisten sind ca. 75MB aufwärts

Was sind denn die größten Einzeldateien? Bzw. deren Größe?
Sollte das Dateisystem parallele Zugriffe gestatten, so passt Parallel.For(Each).
Andernfalls und wenn mehrere Dateien gleichzeitig in den RAM passen, so würde ich 1 Producer zum Lesen vom Dateisystem in den RAM und Environment.ProcessorCount-Consumer zur Prüfsummenberechnung verwenden.

Mit .NET Core, Pipelines, Span lassen sich auch viele Allokationen vermeiden, so dass der GC weniger Arbeit hat und mehr Ressourcen für die eigentliche Arbeit zur Verfügung stehen.

Unabhängig davon wundert es mich schon dass es scheinbar kein OS-eigenes od. fremdes Tool gibt, welches diese Aufgabe erledigen kann.

mfG Gü

14.05.2019 - 18:44 Uhr

Hallo sylvio,

multi-dimensionale Arrays sind in .NET per Design sehr langsam, da für jeden Zugriff ein Methoden-Aufruf durchgeführt werden muss (zusätzlich zu Bound-Checks).

Wesentlich performanter sind sz-Arrays (single dimensional zero based), da bei diesen der JIT direkte Speicherzugriffe -- also ohne Hilfs-Methoden -- erzeugt.
Für mehrdimensionale Tensoren kommen somit "jagged Arrays" in Frage. D.h. float[][][]

Achte dann beim Zugriff auf die Elemente darauf -- v.a. per Schleife -- dass zusammenhängende Daten im Speicher angesprochen werden (memory locality und cache friendlyness).

damit warf der zig Exception bei der Array-Zuweisung; die ich noch nicht vollständig abgefangen hatte

Wie ist das zu verstehen? Bzw. anders rum: wenn du die Exception nicht handeln kannst, so lass es -- dann bekommst du den Fehler wenigsten frühzeitig mit => "fail early".

mfG Gü

14.05.2019 - 16:16 Uhr

Hallo Taipi88,

in Anbetracht der Menge können Hash-Funktionen zu langsam sein und eine kryptografische Sicherheit brauchst du wohl auch nicht (andernfalls nimm die Hinweise von Abt). Daher werden m.E. Prüfsummen ausreichen und wesentlich schneller / performanter sind diese auch (aber dafür weniger bis nicht kryptografisch sicher).

Als Verfahren für die Prüfsumme würde ich Adler-32 verwenden. Dies zum Einen aus dem Bauchgefühl und zum Anderen auch da es Tools wie rsync verwenden, somit gibt es einen praktischen "Beweis" dass es damit funktioniert.

Ich würde zur Prüfung auch ein mehrstufiges Verfahren anwenden. Letzte Dateimodifikation (Zeitstempel), Prüfsumme und Byte-Vergleich* um Kollisionen der Prüfsumme bzw. deren false positives zu eliminieren.

Schau dir aber auch File verification an, vllt. gibt es dort schon Tools auf denen du aufbauen kannst.

* mit Span<byte> lässt sich das mit SequenceEqual sogar vektorisiert durchführen, so dass auch das sehr zügig vonstatten gehen kann.

mfG Gü

13.05.2019 - 12:04 Uhr

Hallo,

WCF

Dafür kann auch .NET 4.8 verwendet werden, das noch "lange" unterstützt werden wird.
Wie MS allerdings "lange" definiert ist eine andere Geschichte.

Für neue Projekte hat Abt Alternativen genannt. Ob sich ein Migrieren von bestehenden WCF-Lösungen auf z.B. gRPC lohnt muss wohl von Fall zu Fall unterschieden werden.

mfG Gü

04.05.2019 - 09:34 Uhr

Hallo MorphieX,

grundsätzlich ist die GPLv2 viral.

Zitat von: GPL2
Die GPL besagt, dass das gesamte kombinierte Programm unter der GPL freigegeben werden muss. Also muss das Modul für die Verwendung unter der GPL verfügbar sein.

Zitat von: GPL2
Ja, denn das Programm, so wie es tatsächlich ausgeführt wird, enthält die Bibliothek.

Aber es gibt Grauzonen:

Zitat von: GPL2-FAQ
Die GPL erfordert nicht Ihre modifizierte Version freizugeben. Sie sind frei, Modifizierungen vorzunehmen und diese privat anzuwenden, ohne sie jemals freizugeben. Das gilt auch für Organisationen (einschließlich Unternehmen); eine Organisation kann eine modifizierte Version erstellen und intern verwenden, ohne sie jemals außerhalb der Organisation freizugeben.

Aber wenn man die modifizierte Version in irgendeiner Weise der Öffentlichkeit freigibt, verlangt die GPL, dass man den modifizierten Quellcode Programmnutzern ebenso unter der GPL bereitstellt.

Vllt. lässt sich damit deine Anforderung schon abdecken, wenn dein Projekt unter GPLv2 gestellt wird, aber du es nicht veröffentlichst.

Allerdings könnte das aus praktischen Gründen schwierig werden:

Zitat von: GPL2-FAQ
Die GPL besagt, dass jedermann, der eine Kopie von Ihnen erhält, das Recht hat, Kopien weiterzugeben, entweder modifiziert oder unmodifiziert. Das Werk darf nicht unter einer restriktiven Grundlage vertrieben werden.

Wenn jemand Sie bittet, eine Vertraulichkeitsvereinbarung für den Empfang von GPL lizenzierter Software zu unterzeichnen, mit einem Copyright der FSF versehen ist, informieren sie uns bitte unverzüglich schriftlich unter
>
darüber.

Der letzte Punkte kann aber durch "Entwicklung" umgangen werden:

Zitat von: GPL2
you can accept a contract to develop changes and agree not to release your changes until the client says ok. This is permitted because in this case no GPL-covered code is being distributed under an NDA.

You can also release your changes to the client under the GPL, but agree not to release them to anyone else unless the client says ok. In this case, too, no GPL-covered code is being distributed under an NDA, or under any additional restrictions.

The GPL would give the client the right to redistribute your version. In this scenario, the client will probably choose not to exercise that right, but does have the right.

Darf ich ein Plugin (eigene PCL) dafür erstellen, welches ich dann unter der GPLv2 lizensiere und in meiner Hauptanwendung zur Laufzeit lade? Kann ich meine Hauptanwendung dann weiterhin unter einer anderen Lizenz vertreiben?

Du darfst dafür ein Plugin erstellen. Unter welcher Lizenz deine Hauptanwendung vertrieben wird, hängt davon ab wie das Plugin geladen wird.
Wird es in den Prozess der Hauptanwendung geladen (z.B. per Activator.CreateInstance), so muss die Hauptanwendung auch unter der GPL2 stehen.
Wird das Plugin in einem eigenen Prozess als eigenständige Anwendung ausgeführt, so ist die Lizenz für das Hauptprogramm wurscht. Die Kommunikaton mit dem Plugin muss dann per IPC erfolgen.
Allerdings:

Zitat von: GPL2
Wenn das Programm Plug-ins dynamisch verbindet, aber die Kommunikation zwischen beiden darauf beschränkt ist die Hauptfunktion des Plug-ins mit einigen Optionen aufzurufen und auf die Rückgabe zu warten, ist das ein Grenzfall.

mfG Gü

PS: ich bin kein Anwalt, daher Abts Rat berücksichtigen (wenn du nicht weismats Rat berücksichtigst 😉).

11.04.2019 - 13:01 Uhr

Hallo Anna85,

mir ist noch nichts klarer geworden 😉

Führ folgendes SQL aus (ersetze das XXX durch den Namen der Ziel-Tabelle):


declare @tableName nvarchar(100) = 'XXX'

select  schema_name(t.schema_id) SchemaName,
        t.name TableName,
        c.column_id ColId,
        c.name ColName,
        t.name DataType,
        c.max_length MaxLength,
        c.precision Precision
from    sys.tables t
join    sys.columns c
    on  t.object_id = c.object_id
left join sys.types typ
    on  c.user_type_id = typ.user_type_id
where   t.name = @tableName
order by SchemaName, TableName, ColId

Und poste das Ergebnis hier (im Ergebnis-Reiter klicken -> Strg + A -> Strg + Umsschalt + C fürs Kopieren).

mfG Gü

11.04.2019 - 12:29 Uhr

Hallo Anna85,

kannst du noch zeige wie die Ziel-Tabelle aufgebaut ist (das Schema), sonst ist es schwer zu erkennen wie die Zuordnung sein soll.

die XML Datei habe ich schon zu SQL Server Management imortiert

Wie meinst du das?

mfG Gü

09.04.2019 - 14:28 Uhr

Hallo dr4g0n76,

Damit wir automatisch überprüfen können, dass beim Online Build nicht Probleme auftreten, die wir schon vor dem Check-In finden hätten können.

Was ist / wäre daran so schlimm (also wenn beim Online-Build Probleme auftreten)?
Wie Abt schon schreibt sollten Änderungen nur via PRs nach master kommen und nie direkt. So werden die Probleme beim Build entdeckt und können behoben werden bevor der PR akzeptiert wird.

(ebenfalls als NuGet Package)

Da kann sich die Katze auch schnell in den Schwanz beißen, wenn mit einem NuGet Paket die Korrektheit der anderen NuGet Pakete validiert werden soll 😉

Geht eher den einfachen Weg und lasst ggf. den Online-Build fehlschlagen, dann wisst ihr sicher dass etwas nicht stimmt.

mfG Gü

05.04.2019 - 12:41 Uhr

Hallo HexEdit,

Hat jemand eine Iddee wie ich das ganze Hardwarebeschleunigt machen kann?

Mit .NET Core 3.0 werden Hardware Intrinsics unterstützt (zumindest für x86/x64, arm folgt später). So kann der Code durch SIMD beschleunigt werden.

Für GPGPU gibt es keine vernüftige Lösung im managed Bereich, da ist native Programmierung (C++ mit CUDA-C als Beispiel) noch die bessere Lösung und dann per P/Invoke in .NET verwenden.

Wenns aber "nur" für Lernzwecke ist, würde ich mich eher auf die Korrektheit vom Code beschränken, sonst kannst du dich schnell in der Hardwarebeschleunigung verlieren.

mfG Gü

05.04.2019 - 12:32 Uhr

Hallo dr4g0n76,

das TRX file in XML umwandelt.

Wie Abt schon schreibt, ist trx auch xml 😉
https://www.nuget.org/packages/trx2junit/ ist vllt. ein passendes dotnet-Tool dazu.

mfG Gü

20.03.2019 - 14:50 Uhr

Hallo baer999,

Erfahrungen mit Stimmen Erkennung und der Zuordnung zu Personen?

Jeder, und zwar seit Geburt an 😉 SCNR.

Hast du nähere Infos zu deinen Anforderungen?
Wie ob es einfach möglich ist "Stimmmuster" von der Zielgruppe einzuholen od. ob es adaptiv dazu lernen soll, etc.

mfG Gü

20.03.2019 - 14:44 Uhr

Hallo Jonas40,

du kannst auch hier im Forum danach suchen. Das Überthema wurde da ein paar mal besprochen, v.a. in Zusammenhang mit konkreteren Anforderungen / Wünschen an das NN.

mfG Gü

04.02.2019 - 12:57 Uhr

Hallo,

Hinweis: beim Betrachten vom geJITetten Code (asm) berücksichtigen dass es sich um die Ausgabe vom Desktop-RyuJIT (".NET Full") handelt und nicht von jenem der in .NET Core verwendet wird. So kann es u.U. gewissen Unterschiede geben.

mfG Gü

27.01.2019 - 11:54 Uhr

Hallo,

...weil das Epsilon von .NET nicht jenem Epsilon in der Numerik entspricht (leider). In der FAQ steht jedoch wie das in der Numerik übliche Epsilon ermittelt werden kann.

mfG Gü