TPL Task.WhenAll (maxmiale sinnvolle Anzahl paralleler Tasks?)



  • Guten Morgen 🙂

    Ich will Tasks parallel Ausführen, und verwende da "Task.WhenAll" (Pseudo Code):

    
    interface IAsyncWorker
    {
    Task AsyncInvoke();
    }
    ....
    
    public async Task DoParallelA()
    {
    IEnumerable<IAsyncWorker> source = ...
    await Task.WhenAll(source.Select(it => it.AsyncInvoke()));
    }
    

    nun habe ich aber oft 1000 Tasks (welche selbst wieder mehrer Tasks(parallel) verarbeiten. und das system lagged extrem.

    Dann bin ich auf den Task.Partitioner gestoßen, und dies Extension hier :

    public static Task ParallelForEachAsync<T>(IEnumerable<T> source, Func<T, Task<int>> funcBody, int maxDoP = 1)
            {
                async Task AwaitPartition(IEnumerator<T> partition)
                {
                    using (partition)
                    {
                        while (partition.MoveNext())
                        {
                            await funcBody(partition.Current);
                        }
                    }
                }
    
                return Task.WhenAll(
                    Partitioner
                        .Create(source)
                        .GetPartitions(maxDoP)
                        .AsParallel()
                        .Select(p => AwaitPartition(p)));
    

    so habe ich die Möglichkeit zu regulieren, wieviel task nur wirklich von den 1000 parallel ausgeführt werden soll.
    Wenn ich das "10" verwendet, werden die 1000 Task schneller ausgeführt, als wenn ich "1000" eingebe!?

    So nun meine Frage: Woran mache ich fest abhängig von meiner Hardware wie der "Task.Scheduler" meine 1000 Task am effizientesten abarbeite, also wie ermittle (am besten Systemunabhängig den "maxDoP" Wert?:)



  • Das solltest du wohl in Abhängigkeit von Environment.ProcessorCount, also der Anzahl der logischen Prozessoren, durchführen.



  • Ok das kling gut, aber wie korreliert die Anzahl der CPU mit der Anzahl der effektiv auszuführenden Tasks (Threads)? Kann man das so sagen, hilft mir der TaskScheduler da ?



  • Wenn die Tasks alle volle Prozessorauslastung benötigen, dann macht es keinen Sinn mehr als Environment.ProcessorCount gleichzeitig auszuführen (da diese sich gegenseitig verlangsamen würden, weil dann mehrere Tasks gleichzeitig auf demselben (virtuellen) Prozessorkern laufen).

    Um das Gesamtsystem nicht komplett auszulasten, kann es daher sogar Sinn machen (bei größeren Mehrkernsystem, z.B. >= 8) sogar weniger parallele Tasks zu starten (aber das kommt eben auf die Priorität und Laufzeit deiner Tasks an).



  • Super danke schön, dann werde ich mal salopp processorcount as max ausführe task angeben;I)



  • Und noch ne Frage zu folgenden Code:

    ich will eine list von async events ausführen, das anfügen und löschen von IAsyncEventHandler kann asynchron passieren, deswegen mach ich eine kopie via lock.. kann ich das auch elliganter lösen?

    public async Task AsyncInvoke(Args input, CancellationToken cancellationToken)
          {
              /* need to sync _requestHandler access */
              IAsyncEventHandler[] Aggregate()
              {
                  lock (_requestHandler)
                  {
                      return _requestHandler.ToArray();
                  }
              }
    
         await  Task.WhenAll(Aggregate());
    
    }
    

    EDIT: So ists wohl elegante;:

    await _semaphoreSlim.WaitAsync(cancellationToken);
    
              try
              {
                await  Task.WhenAll(_requestHandler);
              }
              finally
              {
                  _semaphoreSlim.Release();
              }
    }
    

    wobei ich gelessen habe das dies zweck Threading context mit ConfigureAwait probleme machen kann?


Log in to reply