2012-11-27 19 views
7

Ich bin auf der Suche nach einer einfachen Möglichkeit, eine Aufgabe im Hintergrund zu tun, dann etwas (im Haupt-Thread) zu aktualisieren, wenn es abgeschlossen ist. Es ist in einer niedrigen 'Model' Klasse, so dass ich InvokeOnMainThread nicht aufrufen kann, da ich kein NSObject habe. Ich habe diese Methode:C# einfachen Hintergrund Thread

public void GetItemsAsync(Action<Item[]> handleResult) 
{ 
    Item[] items=null; 
    Task task=Task.Factory.StartNew(() => 
    { 
    items=this.CreateItems(); // May take a second or two 
    }); 
    task.ContinueWith(delegate 
    { 
    handleResult(items); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 
} 

Dies scheint gut zu funktionieren, aber

1) Ist dies der beste (einfachste) Weg?

2) Ich mache mir Sorgen um die lokale Variable:

Item{} items=null 

Was hört auf, dass verschwindet, wenn die Methode zurückgibt, bevor der Hintergrund-Thread beendet?

Danke.

Antwort

2

Ich denke, Ihre Methode leicht ein Single Responsibility Principle verletzt, weil es zu viel zu tun.

Vor allem schlage ich vor, CreateItems zu ändern zurückzukehren Task anstatt es in den GetItemsAsync die Verpackung:

public Task<Item[]> CreateItems(CancellationToken token) 
{ 
    return Task.Factory.StartNew(() => 
    // obtaining the data... 
    {}); 
} 

CancellationToken optional ist, aber können Ihnen helfen, wenn Sie diesen langen Lauf Vorgang abbrechen können.

Mit dieser Methode können Sie GetItemsAsync ganz, weil sein so einfach entfernen Ergebnisse von Ihrem Kunden zu handhaben, ohne diese Delegierten vorbei:

// Somewhere in the client of your class 
var task = yourClass.CreateItems(token); 
task.ContinueWith(t => 
// Code of the delegate that previously 
// passed to GetItemsAsync method 
{}, TaskScheduler.FromCurrentSynchronizationContext()); 

Mit diesem Ansatz Sie freiere Code mit nur einer Verantwortung bekommen. Task Klasse selbst ist ein perfektes Werkzeug für die Darstellung asynchroner Betrieb als first class object. Mit der vorgeschlagenen Technik können Sie Ihre aktuelle Implementierung mit einem falschen Verhalten für Komponententests leicht verspotten, ohne den Code Ihres Clients zu ändern.

+0

Danke, das ist im Prinzip besser. Kann sogar die Task-Variable entfernen yourClass.CreateItems (Token) .ContinueWith (t => { }, TaskScheduler.FromCurrentSynchronizationContext()); Schade, wir brauchen "TaskScheduler.FromCurrentSynchronizationContext()" obwohl. Ich möchte eine möglichst einfache API für den Kunden so seine entweder: 1) yourClass.GetItemsAsync (Delegierter (Artikel [] Artikel) {\t // Tun Sie etwas mit Artikel }); oder 2) yourClass.CreateItems (.) ContinueWith (t => { // Tun Sie etwas mit t.Result }, TaskScheduler.FromCurrentSynchronizationContext()); Also ich brauche wirklich die GetItemsAsync-Methode wirklich. – Bbx

+0

Ja, Sie haben Recht. Aber GetItemsAsync ist viel klarer als vorherige Lösung in Bezug auf OO-Prinzipien und aufgabenbasierte Programmierung. Unter Verwendung der vorgeschlagenen Technik wäre es beispielsweise viel einfacher, auf die wartenden/asynchronen Merkmale C# 5.0 zu migrieren. In Bezug auf OOP ist diese Lösung einfacher, weil sie nicht versucht, das Rad zu erfinden. Und vergessen Sie nicht eine Testbarkeit: In diesem Fall können Sie leicht diese Aufgabe von Mock-Implementierung erhalten und jeden Eckfall von Ihnen Client-Code testen. –

1

Ihr Code sieht gut aus.

Es ist ein perfektes Beispiel für die Verwendung async/await, wenn Sie C# 5 verwenden können, aber wenn nicht, müssen Sie es schreiben, wie Sie mit Fortsetzungen getan haben.

Die items Variable wird in Ihren Lambdas erfasst, also ist das auch in Ordnung.

Wenn Sie ein Lambda schreiben, das eine äußere Variable verwendet, erstellt der C# -Compiler eine Klasse, die die Variable enthält. Dies wird als Schließung bezeichnet und bedeutet, dass Sie auf die Variable in Ihrem Lambda zugreifen können. Hier

+0

Danke für die schnelle Antwort :) Ich hätte fast SO nach meiner letzten Frage über iOS-APIs aufgegeben, um herauszufinden, Datenverkehr wird vollständig ignoriert. Ich denke, es ist gut für einige Dinge und nicht für andere. – Bbx

1

ist ein schöner soultion:

public void GetItemsAsync(Action<Item[]> handleResult) 
    { 
     var task = Task.Factory.StartNew<Item[]>(() => 
     { 
      return this.CreateItems(); // May take a second or two 
     }); 

     task.ContinueWith(delegate 
     { 
      handleResult(task.Result); 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
+0

Danke, das ist schöner :) – Bbx

+0

@Bbx: Sie haben keine Ergebnis-Funktion, weil Sie den Code ändern sollten: Task Task = Task.Factory .... –

+0

Danke, ja, ich habe es gerade gemerkt, nachdem ich meinen Kommentar gepostet habe :) – Bbx

2

Etwas wie folgt aus:

public void GetItemsAsync(Action<Item[]> handleResult) 
    { 
     int Id = 11; 
     Task<Item[]> task = Task.Factory.StartNew(() => CreateItems(Id)); // May take a second or two 
     task.ContinueWith(t => handleResult(t.Result), TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
+0

Schön, danke. Aber persönlich, ich bevorzuge @ Laszlokiss88 Antwort - es ist auch nur 2 Zeilen (im Wesentlichen) und scheint mir einfacher. – Bbx

Verwandte Themen