2016-05-01 13 views
1

Ich habe eine schwierige Zeit zu verstehen, die TPL und ich kann nicht viele klare Artikel darüber finden. Die meisten scheinen vereinfachte Beispiele mit Lambda-Ausdrücken zu verwenden..NET TPL Deklaration Probleme

Ich habe eine C# Funktion

int[] PlayGames(int[][] boardToSearch, int numGamesToPlay) {…} 

ich dieses schraubbaren mit .NET 4.6 TPL in C# machen wollen. Ich möchte bis zu 8 dieser Funktionen gleichzeitig starten, warten bis alle fertig sind, die Ergebnisse erfassen und weitermachen.

Ich kann nicht scheinen, die Typen richtig zu machen und es funktioniert nicht wie erwartet.

Hier ist, was ich bisher habe:

Task<int[]> PlayGames(int[][] boardToSearch, int numGamesToPlay) {…code that takes a long time…} 

private int FindBestMove(int[][] boardToSearch, int numGamesToPlay) 
{ 
    … 
    var taskList = new List<Task>(); 

    taskList.Add(Task.Factory.StartNew(() => { return PlayGames(boardToSearch, numGamesToPlay); })); 
    taskList.Add(Task.Factory.StartNew(() => { return PlayGames(boardToSearch, numGamesToPlay); })); 

    // Tests 
    Task a = taskList.First(); 

    var a1 = a.Result; // NOT ALLOWED!? Viewable in debugger, cannot access it. 

    var b = Task.FromResult(a); 

    var b1 = b.Result; // Works but cannot access child member Result. Debugger sees it, I can’t!?  

    Task.WaitAll(taskList.ToArray()); 
    … 
} 

Hier sind meine Fragen

  1. Wie kann ich den Lambda-Ausdruck () => { return PlayGames(boardToSearch, numGamesToPlay); } entfernen? Ich möchte Func() irgendwie verwenden, aber ich kann nicht für das Leben von mir herausfinden, wie man "Task.Factory.StartNew<int[]>(Func(PlayGames(boardToSearch, numGamesToPlay)))" sagt.

  2. Warum muss ich StartNew() verwenden? Wenn ich taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay)) mache, macht es das synchron !? Wie lautet die korrekte Syntax zum Hinzufügen einer Aufgabe zu einer Liste auf diese Weise? Muss ich einen Func von einigen Arten erklären, die ich zu etwas wie new Task(Func(PlayGames)) übergebe?

  3. Wenn Sie die Variable a nach dem Ausführen der Zeile Task a = taskList.First() betrachten, wird im Debug-Fenster eindeutig ein Member namens Result angezeigt. Und wenn ich dieses Ergebnis erweitere, enthält es die richtigen Daten! Aber wenn ich auf "Add watch" klicken, bekomme ich einen Fehler! Und wenn ich a.Result mache, gibt mir der Compiler denselben Fehler! ?? Der Debugger sagt also, dass er da ist, aber ich kann ihn nicht selbst sehen! Ich kann es von einem Objekt aus durchsuchen, aber nicht direkt. Ich fügte einen Screenshot von diesem ein, damit andere sehen konnten.

  4. Was ist der sauberste Weg, dies mit .NET 4.6 zu tun, während Sie sich von Lambda-Ausdrücken fernhalten. Ich möchte alle Arten und die Erklärungen sehen.

Beigefügt ist ein Screenshot von meinem Debugger, so können Sie sehen, was ich mit .Result

enter image description here

Antwort

2

Lassen Sie uns von oben beginnen bedeuten:

1] Func es nur ein Delegierter ist, das ist der Teil von .net Framework-Bibliotheken. Wenn Sie also () => { return PlayGames(boardToSearch, numGamesToPlay); } übergeben, bedeutet das, dass Sie einfach eine anonyme Methode mit dem Typ Func<int[]> erstellen. Wenn Sie diesen Lambda-Ausdruck einer Variablen zuweisen, können Sie diesen Typ überprüfen. Wenn Sie Lambda nicht verwenden möchten, können Sie eine allgemeine Methode schreiben und sie in die Task einfügen: Task.Factory.StartNew(YourMethodWhichReturnsIntArray).

2] Wenn Sie StartNew() Methode aufrufen, erstellt es nur eine neue Task und beginnt, dies auszuführen. Das ist es. taskList.Add(PlayGames(int[][] boardToSearch, int numGamesToPlay)) - dies nur die Task in die taskList setzen. Wenn in Ihrer PlayGames Methode diese Task wurde nicht gestartet, dann müssen Sie es irgendwann nach.Synchron oder nicht - das Hinzufügen von Task zu Liste ist synchroner Vorgang, aber das Ausführen von Still wird asynchron sein. Jede Syntax kann korrekt sein oder nicht - sie hängt von Komplexität und Realisierung ab. Anstelle von Task.Factory.StartNew() können Sie eine nur Task.Run() Methode verwenden. Es tut dasselbe, aber in einer etwas verkürzten Art und Weise. Und es ist nicht notwendig, eine Funktion zu deklarieren, bevor Sie an die Task übergeben.

3] Ich glaube, das ist, weil die debugger hat die Möglichkeit, auf die Ergebnisse von einem parallelen Thread/Aufgabe warten, aber watcher nicht. Deshalb erhalten Sie einen Fehler. Also würde ich sagen, nicht versuchen, Watcher für die parallelen Threads Ergebnisse hinzuzufügen (nur um die möglichen Fehler zu überspringen).

4] Was ist der sauberste Weg, dies mit .NET 4.6 zu tun, während man sich von Lambda-Ausdrücken fernhält. Ich möchte alle Arten und die Erklärungen sehen.

Wie ich oben sagte, ist es nicht notwendig, das Lambda zu deklarieren. Sie können eine Methode mit der entsprechenden Definition erstellen. Aber hier werden Sie Schwierigkeiten haben, die Parameter an diese Methode zu übergeben (aber sie könnten immer noch gelöst werden). Also, Lambda - ist der einfachste Weg, die Funktion an die Task zu übergeben, weil es leicht Sie Parameter aus dem Bereich erfassen kann, wo diese Lambda's erstellt wurden.

What is the cleanest way - wieder, es kommt darauf an. Jeder von uns hat seinen eigenen saubersten Weg, um neue Aufgaben zu erledigen. Also, ich denke das (siehe unten):

// your function 
int[] PlayGames(int[][] boardToSearch, int numGamesToPlay) {…} 

private int YouMethodToRun8Tasks(int[][] boardToSearch, int numGamesToPlay) 
{ 
    ... 
    var taskList = new List<Task<int[]>>(); 

    // create and run 8 tasks 
    for(var i = 0; i < 8; i++) 
    { 
     // it will capture the parameters and use them in all 8 tasks 
     taskList.Add(Task.Run<int[]>(() => PlayGames(boardToSearch, numGamesToPlay)); 
    } 
    // do something else 
    ... 

    // wait for all tasks 
    Task.WaitAll(taskList.ToArray()); 
    // do something else 
} 

könnte in einigen Fällen als sauberster Weg benannt werden.

Ich hoffe, es wird Ihnen helfen.

+0

Ich habe meinen Code so gemacht, aber wie kann ich auf das Ergebnis zugreifen? Das lässt mich das nicht machen 'foreach (var taskGameResults in taskList) für (int i = 0; i <4; i ++) moveAverageScores [i] + = taskGameResults.Result;' Ich bekomme 'Fehler CS1061 \t' Task 'enthält keine Definition für' Ergebnis 'und keine Erweiterungsmethode' Ergebnis 'ein erstes Argument vom Typ' Task 'konnte gefunden werden (fehlt eine Using-Direktive oder eine Assembly-Referenz ?) ' Dies ist der gleiche Fehler, den ich oben beschrieben habe. Es macht mich verrückt, da dies einfach erscheint! Vielen Dank für Ihre Hilfe. – ejkitchen

+0

Um Zugriff auf das Ergebnis zu erhalten, müssen Sie den Task 'anstelle von' Task' verwenden. Ich habe die Antwort bearbeitet, um das zu zeigen. – MaKCbIMKo

+0

Danke! Das hat funktioniert. Wenn du Zeit hast, kannst du mir einfach zeigen, wie ich alles deklariere, damit ich keinen Lambda-Ausdruck an Task.Run weitergeben muss? Es ist in Ordnung, wenn es einen anderen Typ hinzufügt. Ich möchte nur sehen, wie es gemacht wird. Außerdem habe ich vergessen, die zu Task.Run hinzufügen und es funktionierte immer noch !? Sollte ich nicht einen Fehler bekommen? Es funktioniert in beide Richtungen. Für mich scheinen sie zwei verschiedene Arten zu sein, nein? – ejkitchen