Hallo Leute,
ich habe folgendes Konstrukt:
Task<bool> task = new Task<bool>(() => SpeicherZahlungseingaenge(pZahlungseingaenge));
task.ContinueWith(ZahlungseingaengeGespeichert, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
private void ZahlungseingaengeGespeichert(object pResult, string pParameter1)
{
}
Kann ich der Methode ZahlungseingaengeGespeichert die mittels ContinueWith aufgerufen wird, zusätzlich, neben dem Task.Result, auch einen weiteren Parameter mitgeben? Oder muss ich mir eine entsprechende Klasse für das Task.Result erstellen?
Gruß fungi
Hallo,
hast Du schon mal in die Doku geschaut?
Alle Überladungen von Task.ContinueWith wollen einen Delegaten, der als ersten Parameter die ursprüngliche Task annimmt. Das ist bei Dir nicht der Fall.
Zusätzlich gibt es jede Menge Überladungen, die als zweiten Parameter ein object state
annehmen.
Das Objekt, das dann an den Delegaten übergeben wird, kannst Du beim Aufruf von ContinueWith
mitgeben. Damit kannst Du Dir theoretisch alle möglichen Parameter übergeben, Du musst sie nur in eine Klasse verpacken und innerhalb des Delegaten auf den richtigen Typ zurückcasten.
Das müsste dann so aussehen:
class MyFancyParamsClass
{
public string Param1 { get; set; }
public int Param2 { get; set; }
...
}
private void ZahlungseingaengeGespeichert(Task prevTask, object state)
{
var parameters = (MyFancyParamsClass)state;
...
}
task.ContinueWith(ZahlungseingaengeGespeichert, new MyFancyParamsClass { Param1 = "whatever", Param2 = 7 }, TaskScheduler.FromCurrentSynchronizationContext());
Und wenn Dir das nicht gefällt, kannst Du auch mit einer Lambda-Expression arbeiten, und mit Closures.
Dann würde es so aussehen:
task.ContinueWith(t => ZahlungseingaengeGespeichert(/* hier kannst Du alles mögliche übergeben, z.B. auch t.Result */), TaskScheduler.FromCurrentSynchronizationContext());
Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca
hast Du schon mal in die Doku geschaut?
Jop, als erstes. Allerdings hab' ich es entweder überlesen oder falsch verstanden. Danke für die Aufklärung 😃
Ich empfehle hier auch einen Blick auf async/await
zu werfen.
Das würde dann zu folgendem Code führen
public async Task DoSave( object pZahlungseingaenge, string parameter )
{
var result = await SpeicherZahlungseingaengeAsync( pZahlungseingaenge );
ZahlungseingaengeGespeichert( result, parameter );
}
Dass sie richtig ist.
Tasks nur mit Task.Run() (bzw vor .NET 4.5 Task.Factory.StartNew) erstellen, es sei denn, man weiß ganz genau was man tut und beachten muss.
"Task.Factory.StartNew" vs "new Task(…).Start"
Kurz: wenn du new Task() machst, musst du dich auch um die Synchronisierung kümmern. Machst du das nicht, knallt's.
LaTino
(EDIT: heisst Sir Rufos SpeicherZahlungseingaengeAsync arbeitet auch entsprechend. Die Erstellung des Tasks ist nicht Aufgabe von DoSave.)
"Furlow, is it always about money?"
"Is there anything else? I mean, how much sex can you have?"
"Don't know. I haven't maxed out yet."
(Furlow & Crichton, Farscape)
Hallo LaTino,
vielen Dank für die Anmerkung. Ich habe mir den Artikel durchgelesen. So richtig verstanden warum ich das nicht so machen sollte, wie ich es mache habe ich noch nicht. Könnt Ihr mir ggf. bitte nochmal auf die Sprünge helfen?
One way to fix this is to separate the creation and scheduling of the task, e.g.
Task t = null;
t = new Task(() =>
{
…
t.ContinueWith(…);
});
t.Start();
Das ist doch nichts anderes als mein Code, oder?
Task<bool> task = new Task<bool>(() => SpeicherZahlungseingaenge(pZahlungseingaenge));
task.ContinueWith(ZahlungseingaengeGespeichert, TaskScheduler.FromCurrentSynchronizationContext());
task.Start();
Das ist nicht das Gleiche.
Du unterstellst, dass der Konstruktor mit Parameter genauso funktioniert, wie parameterloser Konstruktor und dann Setzen der Parameters. Das kann das Gleiche sein, es kann es aber auch nicht sein. Gerade bei Multi-Threading sollte man solche Annahmen tunlichst unterlassen.
Hallo,
t = new Task(() =>
{
…
t.ContinueWith(…);
});
Die Zuweisung erfolgt aber erst nach dem Konstruktor.~~ Unsinn. Du benutzt es ja erst in der Action.
async/await
wäre auf jeden Fall zu empfehlen, ich bin da gestern nicht drauf eingegangen, weil explizit nach ContinueWith
gefragt wurde.
Das wurde eingeführt, um einfacheren (und damit besser lesbaren und wartbaren) Code zu schreiben. Alles was hinter einem await
steht, ist sozusagen die Contiuation. Das funktioniert auch wunderbar und der Code wird stark vereinfacht.
Allerdings muss man es wenn man damit anfängt auch durchziehen. Du kannst await
nur in Methoden benutzen, die async
sind und dementsprechend void
, Task
oder Task<T>
zurückgeben (man sollte immer Task statt void nehmen, ausser man schreibt asynchrone EventHandler).
Das ist also viral. Bei neuen Projekten auf jeden Fall zu empfehlen, in alten Projekten ggf. aufwändig einzupflegen, lohnt sich IMO aber trotzdem.
Gruß, MarsStein
Non quia difficilia sunt, non audemus, sed quia non audemus, difficilia sunt! - Seneca