Hallo -
ich habe ein Problem mit meiner Anwendung ich starte um die ~40 Tasks, welche in eine DB schreiben.
Die Tasks werden scheinbar nicht oder nur "sequenziell" ausgeführt.
Starte ich statt den Tasks stattdessen Threads werden viel mehr Datensätze in kürzerer Zeit in die DB geschrieben.
Nun wollte ich mal fragen wieso das so ist bzw. ob ich das ändern könnte?
Codebeispiel wie ich einen Task starte:
Task TestTask = new Task(async () =>
{
// Schreibe in eine DB
}).Start();
MfG
Arg viel schlimmer kannst Du hier async
nicht missbrauchen. Schau Dir an, wie man async mit Tasks richtig verwendet - so jedenfalls nicht.
Das async
kannst Du hier völlig weglassen - bring hier 0.
Korrekt wäre zudem auch das Starten über Task.Factory.StartNew
bzw. Task.Run
Bedenke auch, dass bis auf wenige Ausnahmen Datenbankverbindungen nicht Thread-safe sind.
Für Mass-Inserts verwendet man i.d.R Bulk
.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke für die Antwort benutze nun auch Task.Run.
Aber im Bezug auf das async
weiß ich nicht wie dies anders geregelt werden sollte... ohne dem kann ich in der Funktion keine Asynchronen aufrufe machen.
Oder sollte man dafür wieder eine Task erzeugen?
Parallel Programming with .NET: Task.Run vs Task.Factory.StartNew
Danke für den hinweis mit Bulk
- hört sich vielversprechend an
Ich hab auch schon meine Erfahrungen mit Tasks. Tasks sind dann gut wenn man die CPU wirklich gut auslastet. Das tun deine anscheinend nicht.
Auch wenns ketzerisch klingen mag, nutz einfach Threads für sowas. Die werden nicht im Hintergrund von einer Logik gesteuert die auf deinen Anwendungsfall wahrscheinlich ganz einfach nicht zutrifft.
Davon abgesehen, dass im Hintergrund von Task ein Thread aus dem ThreadPool werkelt (Task ~= Thread, nur mit Scheduler-Logik) läuft ein Task bereits asynchron. Da noch ein async reinzapfen ist so unnötig wie nen Kropf.
Ich vermute also ein insgesamtes Code-Logik Problem und kein Problem von Task/Threads und zeigt eher, dass man das async/await-Pattern nicht verstanden hat.
Hinter async/await steckt nämlich ebenfalls ein Task, den der Compiler erstellt.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Möglich das ich es nicht richtig verstehe, da ich erst neulich mit Tasks angefangen habe bzw. mich momentan immer noch darin einlese.
Evtl. mal ein größeres Beispiel meinerseits:
public class DBInsertClient
{
public Task Routine;
public CancellationTokenSource AsyncCancelTokenSource = null;
public Boolean StartInsertDataBaseRoutine()
{
Routine = Task.Run(async () =>
{
using (MySqlCommand mySQL_CMD = new MySqlCommand("INSERT ......", Connection))
{
mySQL_CMD.Parameters.Add("?Value", MySqlDbType.UInt32).Value = 0;
await mySQL_CMD.ExecuteNonQueryAsync(AsyncCancelTokenSource.Token);
}
}
,(AsyncCancelTokenSource = new CancellationTokenSource()).Token);
return Routine.Status == TaskStatus.RanToCompletion;
}
}
Von dieser konstellation starte ich mehrmals die StartInsertDataBaseRoutine
aber die die CPU steigt nicht in die höhe bzw. die Einträge dauern ewig (muss ich noch auf Bulk umschreiben) aber dies soll den Aufbau zeigen wie ich die Tasks
erzeuge. Es wäre daher sehr nett, wenn mir wer das Beispiel so umgestellt das ich die Pattern
nicht missbrauche?
Die korrekte Schreibweise hier wäre aber
public async Task<Int32> NameDerMethode(...... connection)
{
using (MySqlCommand mySQL_CMD = new MySqlCommand("INSERT ......", connection))
{
mySQL_CMD.Parameters.Add("?Value", MySqlDbType.UInt32).Value = 0;
return mySQL_CMD.ExecuteNonQueryAsync(AsyncCancelTokenSource.Token);
}
}
async/await ist jedenfalls nichts für Anfänger. Man muss das wirklich verstehen, welche "Magie" Microsoft hier im Compiler umgesetzt hat und wofür das ist.
Fazit: dieses Konzept ist das falsche für eine Vielzahl von DB Submits.
Korrekt wären Bulk Inserts, die in MySQL "Load Data" (LOAD DATA INFILE) heißen (.NET -> MySqlBulkLoader ).
Alternativ kann man mehrere Values zeitgleich submitten (Value1, Value2, Value...).
Und dann gehts eben an die Optimierung der Geschwindigkeit. Table sperren, Indexierung temporär deaktivieren, etc etc...
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Du verwendest async/await hier einfach "etwas" kompliziert und ich glaube du hast noch ein Verständnisproblem?
Besser wäre;
public class DBInsertClient
{
public Task Routine;
public CancellationTokenSource AsyncCancelTokenSource = null;
public async void StartInsertDataBaseRoutine()
{
using (MySqlCommand mySQL_CMD = new MySqlCommand("INSERT ......", Connection))
{
mySQL_CMD.Parameters.Add("?Value", MySqlDbType.UInt32).Value = 0;
await mySQL_CMD.ExecuteNonQueryAsync(AsyncCancelTokenSource.Token);
}
}
}
}
ADO.NET bietet dir hierbei bereits eine NonQueryAsync an, diese liefert einen Task den du "awaiten" kannst.
Kontakt & Blog: www.giesswein-apps.at
Und massen inserts gehören immer in eine Transaction, ist hier aber bestimmt hundert mal beschrieben.
... und außerdem kann man das Statement einmal "Preparen", dann in der Schleife nur noch die Parameter befüllen und ausführen lassen. Dann muß das Statement vom DB-Server nur einmal geparst werden und ist dann auch schnell genug.
Ja - ich habe das Prinzip der Tasks wohl nicht richtig verstanden..
Ich dachte die CPU wird bis zum Limit beschäftigt dem ist aber nicht so er startet soviel Tasks wie er der CPU "zumutet" unabhängig davon wie anspruchsvoll die Task ist 😕. Ich dachte jedoch das wenn eine Task im await
geht das die nächste dran kommt.
Für mein vorhaben muss ich wohl einen eigenen kleinen Scheduler coden.
Preparen
meinst du die DB oder die Methode prepare
des MySqlCommand
?
Nein. Er startet auch nicht so viele Tasks wie er will.
DU erstellst die Tasks. Der Unterschied ist dass der Scheduler sich um die Abarbeitung kümmert.
Sprich nur weil Du 1000 Tasks erstellt heisst das nicht, dass 1000 Tasks parallel sind. Sondern eben 8, dann mal 10, dann mal 5. Je nach Ressourcen.
Hingegen startest Du 1000 Threads heisst das, dass auch 1000 Thread aktiv sind.
Wenn Du Dich nicht sauber damit beschäftigst was ein Thread ist, was ein Task ist und was await ist, dann wirst Du immer und immer wieder diese Fehler machen, da Du einfach nicht verstehst, was passiert.
Rumprobieren bei Tasks/Threads ist das aller, aller, aller schlechteste, was man machen kann.
Und nochmal, ich weiß nicht ob Dus nicht verstanden hast oder verstehen willst: Für Dein Vorhaben sind paralelle Tasks oder Threads ein absolutes NoGo!
Es ist NICHT Multi-Threading fähig!
Verwende entweder Bulk Insert oder Transactions und verzichte auf den Task/Thread-Quatsch an dieser Stelle.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Dann verstehe ich nicht wie ich mein Problem sonst lösen soll wenn ich keine Threads/Tasks verwenden soll weil's ein NoGo ist.
Die DB-Einträge sind ja nur ein Teil davon was in der Task/Thread ablaufen soll.
Diese läuft normalerweise in einer Endlosschleife und pullt Daten von einem Webserver und trägt diese wiederum in die DB ein. Dabei möchte ich nicht nur eine Webseite abfragen sondern mehrere... was sollte ich denn sonst tun? Mehrere Anwendungen starten??
Das mit der DB habe ich schon verstanden das diese nicht Multi-Threading fähig ist und ich Bulk nutzen sollte bzw. auch machen werde, das ändert jedoch nichts daran das ich meiner Meinung nach Tasks/Threads benötige um das Problem zu lösen bzw. Parallel Webseiten abzufragen?
Hallo,
Dabei möchte ich nicht nur eine Webseite abfragen sondern mehrere.. Wenn die Seiten getrennt voneinander sind und nichts miteinander zu tun haben könntest Du tatsächlich komplett getrennt behandeln mit mehreren Tasks, wobei jede ihre eigene DB-Verbindung bekommt.
Zur DB: mach mal folgendes:
Teste das erst einmal. Sollte das zu langsam sein, dann evtl mit Bulk Inserts beschäftigen. Bei pallelem Einfügen solltest Du erst einmal prüfen wo der Flaschenhals sich befindet. Außerdem müsstest Du wahrscheinlich für jede parallele Task ein eigenes DB-Connection-Objekt verwenden, der Transaktionsschutz geht dann flöten.
Eigentlich ist die Anforderung von CSharpFreak eine völlig klare Sache für einen Producer/Consumer-Pattern und zum Beispiel TPL-Pipelines.
Die Umgebungsinformation hat hier gefehlt; für DB-Sache selbst sollte man aber eben kein TPL nutzen. Für den Gesamtprozess schon.
**Vorgehen:**1. Collection<String> urls
: hier sind die URLs die abgefragt werden sollen. Wie diese in die Collection kommen: viele Wege führen nach Rom.
webResuls
: hier legen die WebTasks ihre Ergebnisse reinwebResuls
und pushen die Daten in die Datenbank (lieber mit weniger Anfangen und langsam steigern. Irgendwann kommt der Punkt wo mehr Tasks das gesamte System verlangsamen)Wichtig: jeder DbTasks hat seine eigene Datenbankverbindung.
Vorteil der TPL Pipeline ist die absolute Kapselung der einzelnen Aufgaben sowie die sehr einfache horizontale und vertikale Skalierung.
Anmerkung:
Collection<HttpResult>
könnte auch ein Verarbeitungspaket sein in dem X Datenbank-Aufgaben drin liegen, die dann mit einer Transaktion verarbeitet werden.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke - habe es weitestgehend versucht so zu implementieren und läuft soweit auch ganz okay.
Jedoch mit dem Resultat das der MySqlBulkLoader
bei der load
-Methode oft eine exeption wirft.
Fehlermeldung:
MySql.Data.MySqlClient.MySqlException (0x80004005): Deadlock found when trying to get lock; try restarting transaction
Jede Task hat seine eigene MySQL-Verbindung.
Bist Dir sicher? Die Fehlermeldung deutet auf ein Problem mit den Tasks/Threads hin.
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Ja ich bin mir sicher aber habe gerade das hier gelesen
MDSN SqlBulkCopy
Any public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.
Heisst für mich das die Klasse/Instanz zwar Thread-safe ist aber nicht wenn ich mehrere davon erstelle... daraus schließe ich dann auch das ich effektiv nur eine Verbindung benötige da ich sowieso nicht Parallel einfügen kann?
Oder es gibt irgendeine Einstellung am MySql-Server wo ich etwas einstellen kann das es Funtkioniert?
Statische Methoden, die keine Ressourcen teilen, sind immer Thread-Safe.
Es steht aber dran, dass Instanzen einer Klasse eben NICHT garantiert Thread-safe sind.
Du solltest Dir mal durchlesen, was Thread-Safe heisst. Denke Dir fehlen immer noch die Grundlagen von OOP und Thread-Handling. [Artikel] Multi-Threaded Programmierung
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
Danke werde ich mir heute mal durchlesen - Wobei ich eig. weiß was es sein sollte.
Habe mich allerdings auch vertan ich benutze MySqlBulkLoader
mit der load
-Methode.
Da SqlBulkCopy
für MS Datenbanken ist und nicht für MySql 😦