Laden...

Backgroundworder vs. async/await

Erstellt von Kaladial vor 6 Jahren Letzter Beitrag vor 6 Jahren 3.608 Views
Hinweis von Abt vor 6 Jahren

Abgeteilt von GUI blockiert trotz Benutzung von async & await

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

Moin

ich hab mir nur erstmal dein grundproblem durchgelesen.
Wieso machst du das so kompliziert?

Arbeite doch mit backgroundworkern und wenn du in deinem mainwindow etwas ändern willst dann invoke das ganze.

also so nach dem motto:



        private DB db;
        private void MainWindow_Load(object sender, EventArgs e)
        {
             this.db = new DB();
             this.db.Connect(ip, port, servicename, username, password);
             bgwDBAbfrage.RunWorkerAsync();
        }

        private void MainWindow_FormClosing(object sender, EventArgs e)
        {
             this.db.Disconnect();
        }

        private void bgwDBAbfrage_DoWork(object sender, DoWorkEventArgs e)
        {
             string query = "select * from tabelle where irgendwas='JA'";
             this.db.SelectIrgendwas(query, out DBContentTest););
             for (int i = 0; i < DBContentTest.Count; i++)
             {
                    string tmp= DBContentTest[i];
                    setIrgendwas(tmp);
             }
        }

        private void setIrgendwas(string tmp) 
        {
            if (this.InvokeRequired)
            {
                // ruft sich selbst auf
                Action<string> action = new Action<string>(this.setIrgendwas);
                this.Invoke(action, string);
                return;
            }
            this.button1.Text = tmp;
        }

so ungefähr.

mfg Kala

16.807 Beiträge seit 2008
vor 6 Jahren

Backgroundworker sind aber quasi Mottenkiste. async/await ersetzt den BackgroundWorker.

Der Backgroundworker ist nur dann einfacher, wenn das Grundprinzip von Architektur nicht eingehalten wird.
Wer auf eine ordentliche Schichtentrennung setzt, was unbedingt zu empfehlen ist, fährt mit async/await viel besser (bei korrekter Anwendung von async/await durch alle Schichten).

Logik- oder Datenbankcode hat nichts, aber auch gar nichts in der UI zu suchen.

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

was is an bgws mottenkiste? sorry aber funktionieren gut was solls ...
und die db sachen sind bei mir auch ausgelagert, aber um das so veranschaulichen sollte der code ja irgendwas an inhalt haben...

16.807 Beiträge seit 2008
vor 6 Jahren

Klar funktionierts; aber auch Software und die Funktionalität dessen entwickelt sich weiter.
"Was solls, funktioniert!" - da hätten wir heute noch Kutschen. Die haben auch funktioniert.

async/await haben ihre berechtigte Existenz und Priorität.
Datenbanklayer bieten oft nur noch asynchrone Methoden an - zurecht und sinngemäß.

Machste da nen Backgroundworker drum herum haste den ganzen Sinn untergraben.
"Was solls" bringt dann auch nichts mehr 😉

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

naja wenn wir jetzt noch kutschen hätten gäbs weniger luftverschmutzung 😛
nicht alles neue ist auch besser 😉

16.807 Beiträge seit 2008
vor 6 Jahren

Jetzt wirds sinnfrei... dachte Du hattest wirklich interesse zu erfahren, wieso async/await besser ist als der BackgroundWorker. 👍

D
985 Beiträge seit 2014
vor 6 Jahren

naja wenn wir jetzt noch kutschen hätten gäbs weniger luftverschmutzung 😛
nicht alles neue ist auch besser 😉

Dann hätten wir jetzt eine wesentlich höhere Nitrat-Belastung im Grundwasser.

Nicht alles alte war gut.

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

hey du hast mit dem kutschenvergleich angefangen...
bisher haste ja noch nicht wirklich erklärt was an bgws so schlecht ist , was async/await besser machen...
erklärs doch mal an nem kleinen beispiel, wo man auch unterschiede sieht und sich nicht nur auf dein wort verlassen muss das das eine schlechter und das andere besser ist.

2.078 Beiträge seit 2012
vor 6 Jahren

@Kaladial:
Dein Beispiel hat noch einige andere Macken, aber das schieb ich mal darauf, dass es ein Quick&Dirty-Beispiel ist.

Mein Versuch, das auf async zu porten:

private DB db;
private async void MainWindow_Load(object sender, EventArgs e)
{
    this.db = new DB();
    this.db.Connect(ip, port, servicename, username, password);
    await DBAbfrageAsync();
}

private void MainWindow_FormClosing(object sender, EventArgs e)
{
    this.db.Disconnect();
}

private async Task DBAbfrageAsync()
{
    string query = "select * from tabelle where irgendwas='JA'";
    var DBContentTest = await this.db.SelectIrgendwasAsync(query);
            
    for (int i = 0; i < DBContentTest.Count; i++)
        this.button1.Text = DBContentTest[i];
}

Du kannst mir jetzt nicht sagen, dass das komplizierter ist ...
Aber ja, wenn man mit Tasks und async/await nicht klar kommt, ist das durchaus kompliziert. 😛

Wichtig ist dabei aber, dass die SelectIrgendwasAsync-Implementierung auch wirklich async ist.
Entweder indem Du die async-Methoden vom DB-Provider nutzt (was meist in der Komplexität meist kaum einen Unterschied macht) oder Du baust eine Methode sinngemäß wie:

Task<Irgendwas> SelectIrgendwasAsync(string query)
{
    return Task.Run(() => SelectIrgendwas(query));
}

Wobei ich das Starten der Abfrage und das Schreiben der Daten noch auseinander ziehen würde.
Die Abfrage kann ich beim Programmstart anstoßen, damit die schon läuft, während die UI geladen wird. Im Loaded-Event werden dann die zuvor geladenen Daten angezeigt.

Zusätzlich zu dem, was Abt und Sir Rufo schon sagen, sollte es in deinem (und Ticians) Interesse sein, auf dem aktuellen Stand der Dinge zu sein.
Wenn Du z.B. in ein paar Jahren ein altes Programm wieder anfassen musst, was von vorn bis hinten async aufgebaut ist, dann hast Du ein gewaltiges Problem 😉

Außerdem kann ich aus eigener Erfahrung sagen:
Sobald Du einen BackgroundWorker hast, der einen weiteren BackgroundWorker startet, der einen Dritten startet, etc., dann wird's ätzend 😉
Bei async/await sind das einfach nur mehrere awaits nacheinander.
Und das sind noch nicht mal die Probleme, die Du bekommst, wenn Du deine Schichten sauber trennen willst.

16.807 Beiträge seit 2008
vor 6 Jahren

Ich hab doch klar gesagt, wieso async/await als Weiterentwicklung seinen Vorteil und Berechtigung hat.... 🤔
Wenn man jemanden nicht glaubt: einfach mal andere Quellen über Google finden und Pro/Contra anschauen.
Dazu sollte jeder durch eigenes Informationsinteresse ja wohl in der Lage sein 🙂

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

@ Tician: sorry wenn ich dir den thread etwas klaue 😃 aber wir wollen ja alle etwas lernen ggg
Und dem bin ich natürlich auch nicht abgeneigt.

@Palladin007:


Task<Irgendwas> SelectIrgendwasAsync(string query)
{
    return Task.Run(() => SelectIrgendwas(query));
}

Ich hab meine Rückgabewerte bisher die nicht nur string / int / bool, oder wenn ich mehr wie nur einen rückgabe wert brachte, usw waren über outs geregelt.
z.b. halt bei select anfrage hab ich List<string> DBDataTest = new List<string>(); sowas gemacht und die als out mir zurück geben lassen (siehe beispiel).

Schreibe ich dann jetzt meine Rückgabewerte in das "Irgendwas" rein?


Task< List<string>> SelectIrgendwasAsync(string query)
{
    return Task.Run(() => SelectIrgendwas(query));
}

oder mach ich weiterhin:


Task< bool> SelectIrgendwasAsync(string query,out List<string> DBDataTest )
{
    return Task.Run(() => SelectIrgendwas(query, out DBDataTest ));
}

oder wofür steht das Irgendwas?

1.029 Beiträge seit 2010
vor 6 Jahren

Hi,

da ja nun schon gestritten wird - finde folgenden, verlinkten Vergleich eigentlich ziemlich gut - zeigt ehrlich Vor- und Nachteile auf:

http://blog.stephencleary.com/2013/05/taskrun-vs-backgroundworker-round-1.html

Der Gewinner: async/await

Das heisst ja nicht, dass der BGW schlecht ist.

LG

16.807 Beiträge seit 2008
vor 6 Jahren

Ich hab meine Rückgabewerte bisher die nicht nur string / int / bool, oder wenn ich mehr wie nur einen rückgabe wert brachte, usw waren über outs geregelt.

Dann ist das ja schon der erste Missbrauch des Thema Returns. Damit hast Dir ja selbst ins Fleisch geschnitten. Quasi "faul" umgesetzt, wenn man das so sagen kann - und async damit selbst verbaut.

out hat seine Legitimität, zB für Try-Methoden; aber doch nicht um "mehrere Elemente (einer asynchronen Operation)" zurück zu geben.
Mehrere Objekte würde man sauber durch eine eigene Klasse zurück geben (oder halb-sauber mit Tuples, wobei die durch named tuples jetzt sauberer geworden sind).
Oder bei Tasks eben durch mehrere Aufrufe, um - je nach Logik - parallel Dinge zu laden; wo es dann auch jeweils nur ein Rückgabeobjekt gibt.

async/await ist ein Gesamtkonzept, das durchgängig ist; BGW nicht.

2.078 Beiträge seit 2012
vor 6 Jahren

Ich hab meine Rückgabewerte bisher die nicht nur string / int / bool, oder wenn ich mehr wie nur einen rückgabe wert brachte, usw waren über outs geregelt.
z.b. halt bei select anfrage hab ich List<string> DBDataTest = new List<string>(); sowas gemacht und die als out mir zurück geben lassen (siehe beispiel).

Siehe Abt's Antwort.

Bei Tasks ist weder out noch ref erlaubt.
Das ist auch richtig so, immerhin geht der Aufruf der Methode (wenn Du nicht awaitest) einfach weiter, bevor die Methode dem out-Parameter einen Wert geben kann.

Das "Irgendwas" stand nur für irgendein Ergebnis von der Datenbank, kein produktives Beispiel.
Wenn Du unbedingt ein List<string> zurück geben willst, dann so:

Task<List<string>> SelectIrgendwasAsync(string query)
{
    return Task.Run(() => SelectIrgendwas(query));
}

Übrigens: Es ist auch ein IAsyncEnumerable im Gespräch
Ich weiß allerdings nicht, wie da der aktuelle Stand ist bzw. wann das kommt. Ich kann nur hoffen, dass es kommt.

Wenn da jemand mehr weiß? =)

16.807 Beiträge seit 2008
vor 6 Jahren

IAsyncEnumerable<T> gibt es bereits, zB wer mit der Azure Service Fabric arbeitet hat es im SDK; ist aber noch nicht im Standard generell enthalten.
Ich meine aber gelesen zu haben, dass es in .NET Core geben wird im Rahmen der Channels API.

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

Heißt also, wenn ich anfang und will verschiedene datentypen usw zurück geben bau ich mir erstmal ne klasse pack da mein ganzes zeug rein und pack die dann in die rückgabewerte?

Heißt ich packe statt der List<string> meine klasse rein die z.b. nen bool und ne List<string> beinhaltet. und die pack ich dann in


Task<MyClass> SelectIrgendwasAsync(string query)

16.807 Beiträge seit 2008
vor 6 Jahren

Das ist durchaus ein übliches Vorgehen und legitim, ja. Und kein Missbrauch von Sprachfeatures wie out.
Je nach Situation eben auch die Aufsplittung in mehrere Methoden.

string myStringReturnValue = await GetAnyStringAsync();
Int32 myIntReturnValue = await GetRandomIntAsync();
MyClass myClassReturnValue = await GetClassAsync();

out ist auch im Rahmen von Unit Tests umständlicher zu Testen als ein Rückgabewert.
Davon abgesehen sprengt out auch die Idee hinter "unit".

Mir fällt aktuell kein legitimer Einsatz von out ein, ausser eben Try-Methoden und PInvoke.

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

ja ok verstehe...
sorry ich komm aus der c++ welt und mit c# beschäftige ich mich erst ca 1/2 jahr und da hab ich halt sachen die meine kollegen programmiert hab übernommen.
was funktioniert wird halt weiter verwendet 😉 wenns aber bessere wege gibt bin ich immer dankbar für hinweise

2.078 Beiträge seit 2012
vor 6 Jahren

Wenn List<string> mehrere verschiedene Werte meint, dann solltest Du daraus eine Klasse mit entsprechenden Properties machen.

Also in etwas so:

public class Person
{
    public long Id { get; set; }
    public string Name { get; set; }
    public DateTime Geburtstag { get; set; }
}

public async Task<Person> GetPersonByIdAsync(long id)
{
    // ...
}
public async Task<Person[]> GetPeopleAsync()
{
    // ...
}

Wenn Du das zuende denkst, landest Du beim RepositoryPattern.

Oder Du nimmst ein ORM, was das alles schon tut.

16.807 Beiträge seit 2008
vor 6 Jahren

Wieso kann die Datenbank kein List? 🤔 List<string>, kann sie durchaus nicht mappem, List<Person> schon - keine Notwendigkeit für ein Array.
ADO.NET, DocumentDB, Sqlite, MongoDB, Postgres.. alle können sie List.

Das vollständige Laden einer SQL Tabelle in eine List<T> macht aber selten Sinn (>braucht man wirklich immer alles?)

2.078 Beiträge seit 2012
vor 6 Jahren

Aber nur im Sinne von ForeignKey-Beziehungen.
Die Liste beinhaltet dann die Einträge, die eine Referenz auf die Aktuelle haben.

Also:

public class Person
{
    public long Id { get; set; }
    public string Name { get; set; }
    public DateTime Geburtstag { get; set; }
    public Adresse Adresse { get; set; }
}
public class Adresse
{
    public long Id { get; set; }
    public string Ort { get; set; }
    public string Straße { get; set; }
    public string PLZ { get; set; }
    public string Hausnummer { get; set; }

    public IList<Person> Personen { get; set; }
}

Eine String-Liste kann sie nicht, die müsste ich z.B. mit Trennzeichen verkettet in eine einzelne String-Column schreiben.
Oder hab ich was verpasst? 🤔

16.807 Beiträge seit 2008
vor 6 Jahren

Wenn Du Navigationproperties meinst, dann wäre das aber

public virtual ICollection<Person> Personen { get; set; }

sofern wir von ADO.NET sprechen.

(Wobei eine Adresse eigentlich ein typisches Beispiel für gewollte Redundanzen ist - das nur Nebenbei).

Im Gegensatz zu ICollection hat IList nämlich eine Random-Ordered Structure, die im DAL bei Navigationproperties nicht so viel sinn macht.
Bei MongoDB gibt dann die MongoDbCollection statt IList, und bei DocumentDb eben die DocumentDBCollection (sofern man die DocumentDB API nimmt).

2.078 Beiträge seit 2012
vor 6 Jahren

Stimmt, ICollection<T> wäre besser, auch wenn's nicht notwendig ist.
NHibernate und EntityFramework kommen auch mit IList<T> klar, nur die Reihenfolge wird ignoriert.

Was ich aber ursprünglich meine, ist, dass eine Column keine List<string> beinhalten kann. ((soweit ich weiß?)

Und wenn das kein einziger Column-Wert sein soll, sondern mehrere verschiedene Inhalte, dann sollte es dafür eine eigene Klasse mit entsprechenden Properties geben.

16.807 Beiträge seit 2008
vor 6 Jahren

Jap, Collections von primitiven Typen kann keine relationale Datenbank, daher auch kein ORM. NoSQL kanns.
Die Aussage hatte nur nicht zum Beispiel gepasst - daher mein Hinweis.

2.078 Beiträge seit 2012
vor 6 Jahren

Jetzt sind wir auf einem Nenner ^^
Ich hab's oben angepasst, ich hoffe, so ist's verständlicher =)

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

Hmmm das eine datenbank keine strings kann...
naja es sind vchars die aber im eigentlichem sinne strings sind.

2.078 Beiträge seit 2012
vor 6 Jahren

Ich rede von String-Listen, nicht Strings
Die kannst Du nur erreichen, wenn Du dir ein eigenes Format ausdenkst, z.B. durch Pipe getrennt in einen String verketten.

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

naja wenn ich ne select abfragen an ne db schicke bekomm ich (wenns nicht weiter eingeschränkt wird) immer mehrere ergebnisse.
natürlich geh ich die in ner whileschleife durch.
und diese, z.b. string rückgaben, add ich dann in ne liste die meine funktion zurück gibt...
daher ne stringliste. ich will ja eben so wenig wir möglich verschiedene datentypen durch mein ganzes programm schleifen, also brech ich das auf das kleinste gemeinsame runter und übergeb nicht das ganze result quer durch mein programm.

2.078 Beiträge seit 2012
vor 6 Jahren

ich will ja eben so wenig wir möglich verschiedene datentypen durch mein ganzes programm schleifen, also brech ich das auf das kleinste gemeinsame runter und übergeb nicht das ganze result quer durch mein programm.

Und warum?
Der Runtime oder deinem PC ist's egal, wie viele Datentypen Du umher schleifst.
Das einzige was Du damit erreichst ist überall viele string-Listen wo keiner anhand des Typs erkennen kann, was drin steht.

Du solltest stattdessen lieber darauf achten, den Code übersichtlich zu halten.
Klar, Du kannst auch diese vier Methoden bereit stellen:

string[] GetOrte();
string[] GetStraßen();
string[] GetPLZs();
string[] GetHausnummern();

Dann hast Du vier string-Arrays, bei denen Du anhand des Indexes die zusammengehörigen Werte raus picken musst.

Viel einfacher ist doch:

Adresse[] GetAdressen();

Da hab ich dann meine Ergebnisse in einem Objekt zusammen gefasst und kann mit Properties anstatt Indizes arbeiten.

1.040 Beiträge seit 2007
vor 6 Jahren

Die Diskussion ist irgendwie ziemlich entgleist. 😉

Die Rückgabewerte einer Methode haben per se erstmal überhaupt nichts damit zu tun, ob man Backgroundworker oder async/await nutzt.

Auch eine "normale" Funktion die so aussieht:

void GetAdress(out string street, out int number, out string zipCode, out string location);

ist doch ziemlicher murks und sollte eher so aussehen:

Adress GetAdress();

class Adress
{
    public string Street { get; }
    public int Number { get; }
    public string ZipCode { get; }
    public string Location { get; }
}

Das Grundkonzept sollte schon vor async/await so aussehen.
Nutzt man jetzt async/await wird aus dem Rückgabewert eben Task<Adress>, ganz einfach.

PS: Ich denke nicht, dass er überall List<string> durch das Programm schleift, das war wohl eher nur ein Beispiel. 😉

Edit sagt noch @Kaladial:
Task<Irgendwas> steht für Task<TResult>, d.h. das Ganze ist generisch. Zu dem Thema spuckt Google einiges aus.

K
Kaladial Themenstarter:in
54 Beiträge seit 2017
vor 6 Jahren

@pille: genau 😃

also ich hab mal vor langer langer zeit gelernt das man n bissi strukturiert arbeitet
und da gehört es halt für mich dazu z.b. SQL Code nicht mal dort n stück stehn hat und an ne ganz anderen stelle den rest... das wird unleserlich. ich will wenn ich meinen quellcode angucke halt
auch noch nachvollziehen was ich mache. und in den jahren hab ich mir da so einiges angewöhnt was andere vielleicht quatsch finden, ich damit aber gut klar komme. und das ist ja nun mal das was zählt...
und es führen nun mal viele wege nach rom. und ob die nu neu sind oder alt is dabei nicht unbedingt wichtig, solange man ankommt. code muss funktionell sein. für was anderes hat man selten zeit.
und ja bei c# kenn ich mich noch nicht so aus, ich hab aber trotzdem schon mehrer programme geschrieben die alle das machen was sie sollen.

so und nu ist gut. ich werd das mal mit dem asynch und await ausprobieren und wenns mir gefällt nehm ich das wenn nicht bleib ich bei dem was für mich funktioniert und das sind nun mal die bgws 😃

A
764 Beiträge seit 2007
vor 6 Jahren

also ich hab mal vor langer langer zeit gelernt das man n bissi strukturiert arbeitet
und da gehört es halt für mich dazu z.b. SQL Code nicht mal dort n stück stehn hat und an ne ganz anderen stelle den rest... das wird unleserlich. ich will wenn ich meinen quellcode angucke halt
auch noch nachvollziehen was ich mache. und in den jahren hab ich mir da so einiges angewöhnt was andere vielleicht quatsch finden, ich damit aber gut klar komme. und das ist ja nun mal das was zählt...

Mir ist es wichtig Code so zu schreiben, dass auch andere ihn verstehen können. Das wirkt sich ganz positiv auf die Teamarbeit aus. Die meisten arbeiten ja nicht alleine am Quellcode.