3
var finalList = new List<string>(); 
var list = new List<int> {1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ................. 999999}; 

var init = 0; 
var limitPerThread = 5; 

var countDownEvent = new CountdownEvent(list.Count); 

for (var i = 0; i < list.Count; i++) 
{ 
    var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
    new Thread(delegate() 
        { 
         Foo(listToFilter); 
         countDownEvent.Signal(); 
        }).Start();  
    init += limitPerThread; 
} 

//wait all to finish 
countDownEvent.Wait(); 


private static void Foo(List<int> listToFilter) 
{ 
    var listDone = Boo(listToFilter); 
    lock (Object) 
    { 
     finalList.AddRange(listDone); 
    } 
} 

Dies gilt nicht:Warum einfache Multi-Task nicht funktioniert, wenn Multi-Thread tut?

var taskList = new List<Task>(); 

for (var i = 0; i < list.Count; i++) 
{ 
    var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
    var task = Task.Factory.StartNew(() => Foo(listToFilter)); 
    taskList.add(task); 
    init += limitPerThread; 
} 

//wait all to finish 
Task.WaitAll(taskList.ToArray()); 

Dieser Prozess mindestens 700 Fäden am Ende erstellen müssen. Wenn ich mit Thread arbeite, funktioniert es und erstellt alle von ihnen. Aber mit Task ist es nicht .. Es scheint wie es nicht beginnt, multiples Tasks async.

Ich möchte wirklich wissen, warum .... irgendwelche Ideen?

EDIT

Eine andere Version mit PLINQ (wie vorgeschlagen).

var taskList = new List<Task>(list.Count); 
Parallel.ForEach(taskList, t => 
        { 
         var listToFilter = list.Skip(init).Take(limitPerThread).ToList(); 
         Foo(listToFilter); 
         init += limitPerThread; 
         t.Start(); 
        }); 
Task.WaitAll(taskList.ToArray()); 

EDIT2:

public static List<Communication> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 

     try 
     { 
      for (var i = 0; i < listIps.Count(); i++) 
      { 
       var oPing = new Ping().Send(listIps.ElementAt(i).IpAddress, 10000); 
       if (oPing != null) 
       { 
        if (oPing.Status.Equals(IPStatus.TimedOut) && listIps.Count() > i+1) 
         continue; 
        if (oPing.Status.Equals(IPStatus.TimedOut)) 
        { 
         communication.Result = "NOK"; 
         break; 
        } 
        communication.Result = oPing.Status.Equals(IPStatus.Success) ? "OK" : "NOK"; 
        break; 
       } 
       if (listIps.Count() > i+1) 
        continue; 
       communication.Result = "NOK"; 
       break; 
      } 
     } 
     catch 
     { 
      communication.Result = "NOK"; 
     } 
     finally 
     { 
      listResult.Add(communication); 
     } 
    } 

    return listResult; 
} 
+0

Können Sie die Elemente eins nach dem anderen verarbeiten? Das heißt, können Sie 'Boo()' für ein einzelnes Element und nicht für eine Liste aufrufen? –

+0

Ich kann sie nicht einzeln verarbeiten. Es ist ein Prozess, um multiple ips-Adresse zu pingen ... Ich muss alle von ihnen pingen (+ - 3000) in weniger als 2 Minuten. Und ich muss das Ergebnis speichern. –

+2

Warte ... nur ** was macht 'Boo()' eigentlich? Es erzeugt nicht einfach einen "Ping" -Prozess, oder? –

Antwort

6

Task s sind Multithreading NICHT. Sie können dafür verwendet werden, aber meistens werden sie tatsächlich für das Gegenteil verwendet - Multiplexing auf einem einzigen Thread.

Um Aufgaben für Multithreading zu verwenden, empfehle ich, Parallel LINQ zu verwenden. Es hat viele Optimierungen in es bereits, wie intelligente Partitionierung Ihrer Listen und nur Laichen wie viele Threads wie es CPU-Kerne Ar usw.


Task und async Um zu verstehen, denken Sie an es auf diese Weise - ein typisches Arbeitsbelastung beinhaltet oft IO, auf die gewartet werden muss. Vielleicht lesen Sie eine Datei oder fragen einen Webservice ab oder greifen auf eine Datenbank zu, oder was auch immer. Der Punkt ist - Ihr Thread muss eine loooong Zeit (mindestens in CPU-Zyklen) warten, bis Sie eine Antwort von einem weit entfernten Ziel erhalten.

In den Olden Days ™ bedeutete das, dass dein Thread gesperrt wurde (suspendiert), bis diese Antwort kam. Wenn du in der Zwischenzeit etwas anderes machen wolltest, musst du einen neuen Thread erstellen. Das ist machbar, aber nicht zu effizient. Jeder OS-Thread trägt einen signifikanten Overhead (Speicher, Kernel-Ressourcen) mit sich. Und Sie könnten am Ende mehrere Threads haben, die aktiv die CPU brennen, was bedeutet, dass das Betriebssystem zwischen ihnen wechseln muss, so dass jeder ein bisschen CPU-Zeit bekommt und diese "Kontextwechsel" ziemlich teuer sind.

async ändert diesen Workflow. Jetzt können mehrere Workloads auf demselben Thread ausgeführt werden. Während ein Teil der Arbeit das Ergebnis einer weit entfernten Quelle ist, kann ein anderer eingreifen und diesen Thread verwenden, um etwas anderes Nützliches zu tun. Wenn diese zweite Auslastung zu ihrer eigenen await wird, kann die erste aufwachen und fortfahren.

Immerhin macht es nicht sinnvoll, mehr Threads zu spawnen als es CPU-Kerne gibt. Sie werden auf diese Weise nicht mehr arbeiten. Im Gegenteil: Es wird mehr Zeit für das Wechseln der Threads benötigt und weniger Zeit für sinnvolle Arbeit.

Das ist, was die Task/async/await ursprünglich für entwickelt wurde. Allerdings hat Parallel LINQ auch davon profitiert und es für Multithreading wiederverwendet.In diesem Fall kannst du es auf diese Weise betrachten - die anderen Threads sind das, was dein Haupt-Thread ist, das "ferne Ziel", auf das dein Haupt-Thread wartet.

+0

guter Text, Bruder ... Ich habe PLINQ auch versucht, aber ohne Erfolg .. Ich werde die Frage in der Implementierung bearbeiten. Ich schätze es, wenn Sie einen Blick darauf werfen könnten. –

+2

"Aufgaben sind nicht Multithreading" - Nein, nicht immer, aber StartNew() bedeutet, sie zumindest zum ThreadPool zu pushen. –

+0

Richtig, aber es ist ein häufiges Missverständnis, dass async == Multithreading. Und es scheint, dass das OP auch so gedacht hat. –

5

Aufgaben werden im Thread-Pool ausgeführt. Dies bedeutet, dass eine Handvoll Threads einer großen Anzahl von Aufgaben dienen. Sie haben Multi-Threading, aber keinen Thread für jede erstellte Aufgabe.

Sie sollten Aufgaben verwenden. Sie sollten versuchen, so viele Threads wie Ihre CPU zu verwenden. Im Allgemeinen macht der Thread-Pool dies für Sie.

0

Wie haben Sie die Leistung gemessen? Denkst du, dass die 700 Threads schneller funktionieren als die 700 Tasks, die von 4 Threads ausgeführt werden? Nein, würden sie nicht.

Es scheint, wie es ist nicht ein Vielfaches Aufgaben async

beginnen Wie Sie kam auf diese haben? Wie in anderen Kommentaren und anderen Antworten empfohlen, müssen Sie wahrscheinlich eine Thread-Erstellung entfernen, da nach der Erstellung von 700 Threads die Systemleistung beeinträchtigt wird, da Ihre Threads für die Prozessorzeit gegeneinander kämpfen, ohne dass die Arbeit schneller erledigt wird .

So müssen Sie die async/await für Ihre IO-Operationen in die Foo-Methode mit SendPingAsync Version hinzufügen. Auch Ihre Methode vereinfachter werden könnte, wie viele Kontrollen für eine listIps.Count() > i + 1 Bedingungen sind nutzlos - Sie tun es in dem for Bedingungsblock:

public static async Task<List<Communication>> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 

     try 
     { 
      var ping = new Ping(); 
      communication.Result = "NOK"; 

      for (var i = 0; i < listIps.Count(); i++) 
      { 
       var oPing = await ping.SendPingAsync(listIps.ElementAt(i).IpAddress, 10000); 
       if (oPing != null) 
       { 
        if (oPing.Status.Equals(IPStatus.Success) 
        { 
         communication.Result = "OK"; 
         break; 
        } 
       } 
      } 
     } 
     catch 
     { 
      communication.Result = "NOK"; 
     } 
     finally 
     { 
      listResult.Add(communication); 
     } 
    } 

    return listResult; 
} 

Andere Probleme mit dem Code sind, dass PLINQ Version ist nicht THREAD:

init += limitPerThread; 

Dies kann während der parallelen Ausführung fehlschlagen.

private async Task<List<PingReply>> PingAsync(List<Communication> theListOfIPs) 
{ 
    Ping pingSender = new Ping(); 
    var tasks = theListOfIPs.Select(ip => pingSender.SendPingAsync(ip, 10000)); 
    var results = await Task.WhenAll(tasks); 

    return results.ToList(); 
} 

Und tun diese Art der Prüfung (try/catch Logik der Einfachheit halber entfernt): Sie können einige Hilfsmethode, wie in this answer einführen

public static async Task<List<Communication>> Foo(List<Dispositive> listToPing) 
{ 
    var listResult = new List<Communication>(); 
    foreach (var item in listToPing) 
    { 
     var listIps = item.listIps; 
     var communication = new Communication 
     { 
      IdDispositive = item.Id 
     }; 
     var check = await PingAsync(listIps); 
     communication.Result = check.Any(p => p.Status.Equals(IPStatus.Success)) ? "OK" : "NOK"; 
    } 
} 

Und Sie sollten wahrscheinlich Task.Run statt Task.StartNew verwenden für sein Vergewissern Sie sich, dass Sie den UIhread nicht blockieren.

Verwandte Themen