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
Diese Punkte kannst du i.d.R. deaktivieren, außer sie werden beim Debuggen tatsächlich benötigt.
mfG Gü
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 Append
s 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ü
Hallo RafaelVogt,
vorsorglich der Hinweis zu [Hinweis] Wie poste ich richtig? Punkt 1.1.
Google-Suche nach bitweise operationen -> Bitweiser Operator
mfG Gü
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ü
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ü
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ü
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ü
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ü
Hallo tobi45f,
richtig? Vermutlich muss ich das Datenbank lesen+schreiben noch asyncen?!
Ja, alles async -- async ist viral 😃
mfG Gü
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ü
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ü
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 Task
s 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 Thread
s 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
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ü
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ü
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ü
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ü
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ü
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ü
Wegen [Hinweis] Wie poste ich richtig? Punkt 2.2 ==> geschlossen
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ü
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ü
Hallo,
Konfigurationsmiddleware
Die gibt es nicht (zumindest per Standard ASP.NET Core). ConfigurationBuilder hingegeben schon.
mfG Gü
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ü
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ü
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ü
Hallo Anna85,
geht es um das selbe Problem wie bisher od. ist es ein Neues?
mfG Gü
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ü
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ü
Hallo zusammen,
wie vllt. bekannt ist wird mit C# 8.0 der Kampf dem Billion Dollar Mistake (© Tony 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ü
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ü
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ü
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ü
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ü
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ü
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ü
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ü
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ü
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 😉).
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ü
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ü
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ü
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ü
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ü
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ü
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ü
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ü
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ü