Laden...

Task.ContinueWith mit Parameter aufrufen

Erstellt von fungi35 vor 6 Jahren Letzter Beitrag vor 6 Jahren 2.590 Views
F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren
Task.ContinueWith mit Parameter aufrufen

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

3.170 Beiträge seit 2006
vor 6 Jahren

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

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

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 😃

D
985 Beiträge seit 2014
vor 6 Jahren

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 );
}

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

Hallo Sir Rufo,

welchen Vorteil gegenüber meiner Lösung hätte das?

3.003 Beiträge seit 2006
vor 6 Jahren

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)

F
fungi35 Themenstarter:in
42 Beiträge seit 2015
vor 6 Jahren

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();
W
872 Beiträge seit 2005
vor 6 Jahren

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.

3.170 Beiträge seit 2006
vor 6 Jahren

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