Fait Parallèle.Pourchaque limite le nombre de threads actifs?

étant donné ce code:

var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
    DoSomething(someString);
});

est-ce que les 1000 fils vont frayer presque simultanément?

94
demandé sur johnnyRose 2009-07-11 22:13:36

5 réponses

Non, il ne démarre pas 1000 threads - oui, il limite combien de threads sont utilisés. Les Extensions parallèles utilisent un nombre approprié de noyaux, basé sur combien vous avez physiquement et combien sont déjà occupés. Il alloue du travail pour chaque noyau et utilise ensuite une technique appelée voler le travail pour laisser chaque thread traiter sa propre file d'attente efficacement et n'a besoin de faire n'importe quel accès cross-thread coûteux quand il a vraiment besoin.

regardez le PFX Team Blog pour charges d'informations sur la façon dont il répartit le travail et toutes sortes d'autres sujets.

Notez que dans certains cas, vous pouvez spécifier le degré de parallélisme que vous voulez, aussi.

138
répondu Jon Skeet 2009-07-11 22:17:14

sur une seule machine... Parallèle.ForEach partitions (morceaux) de la collection il travaille sur entre un certain nombre de threads, mais ce nombre est calculé sur la base d'un algorithme qui prend en compte et semble surveiller continuellement le travail fait par les threads qu'il attribue à L'ForEach. Ainsi, si la partie du corps de L'Antéach appelle à des fonctions de blocage/io-bound de longue durée qui laisseraient le thread dans l'attente, l'algorithme va engendrer plus les fils et la répartition de la collection entre eux . Si les threads se terminent rapidement et ne bloquent pas les threads IO, par exemple, en calculant simplement quelques nombres, l'algorithme va augmenter (ou même diminuer) le nombre de threads jusqu'à un point où l'algorithme considère optimum pour le débit (temps moyen d'achèvement de chaque itération) .

fondamentalement le pool de thread derrière toutes les diverses fonctions de bibliothèque parallèle, fonctionnera un nombre optimal de threads à utiliser. Le nombre de cœurs de processeur physique ne constitue qu'une partie de l'équation. Il n'y a pas de relation simple entre le nombre de noyaux et le nombre de threads générés.

Je ne trouve pas la documentation concernant l'Annulation et la manipulation des threads de synchronisation très utile. Espérons que MS puisse fournir de meilleurs exemples dans MSDN.

N'oubliez pas, le code du corps doit être écrit pour fonctionner sur plusieurs filetage, avec toutes les considérations habituelles de sécurité filetage, le cadre ne fait pas abstraction de ce facteur... encore.

24
répondu Terry 2017-04-12 19:27:38

il élabore un nombre optimal de threads basé sur le nombre de processeurs/cœurs. Ils ne seront pas tous spawn à la fois.

5
répondu Colin Mackay 2009-07-11 18:18:32

Voir Does Parallel.Pour utiliser une Tâche par itération? pour une idée de "modèle mental" à utiliser. Toutefois, l'auteur déclare qu'en fin de compte, il est important de se rappeler que les détails de la mise en œuvre peuvent changer à tout moment."

5
répondu Kevin Hakanson 2009-07-11 18:55:02

grande question. Dans votre exemple, le niveau de parallélisation est assez faible, même sur un processeur quad core, mais avec un peu d'attente, le niveau de parallélisation peut être assez élevé.

// Max concurrency: 5
[Test]
public void Memory_Operations()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    Parallel.ForEach<string>(arrayStrings, someString =>
    {
        monitor.Add(monitor.Count);
        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

regardez maintenant ce qui se passe lorsqu'une opération d'attente est ajoutée pour simuler une requête HTTP.

// Max concurrency: 34
[Test]
public void Waiting_Operations()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    Parallel.ForEach<string>(arrayStrings, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(1000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

Je n'ai pas encore fait de changements et le niveau de concurrence/parallélisation a sauté drammatically. La concurrence peut avoir ses limites augmenté de ParallelOptions.MaxDegreeOfParallelism .

// Max concurrency: 43
[Test]
public void Test()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(1000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

// Max concurrency: 391
[Test]
public void Test()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(100000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

j'ai recommandons le réglage ParallelOptions.MaxDegreeOfParallelism . Il n'augmentera pas nécessairement le nombre de threads utilisés, mais il vous assurera de ne lancer qu'un nombre raisonnable de threads, ce qui semble être votre préoccupation.

enfin pour répondre à votre question, non vous n'obtiendrez pas tous les fils pour commencer à la fois. L'Utilisation En Parallèle.Invoquez si vous cherchez à invoquer en parallèle parfaitement, par exemple en testant les conditions de course.

// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623368346
// 636462943623368346
// 636462943623373351
// 636462943623393364
// 636462943623393364
[Test]
public void Test()
{
    ConcurrentBag<string> monitor = new ConcurrentBag<string>();
    ConcurrentBag<string> monitorOut = new ConcurrentBag<string>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(DateTime.UtcNow.Ticks.ToString());
        monitor.TryTake(out string result);
        monitorOut.Add(result);
    });

    var startTimes = monitorOut.OrderBy(x => x.ToString()).ToList();
    Console.WriteLine(string.Join(Environment.NewLine, startTimes.Take(10)));
}
3
répondu Timothy Gonzalez 2017-11-14 22:18:46