Parallel Linq - использовать больше потоков, чем процессоров (для задач без привязки к процессору) - PullRequest
10 голосов
/ 04 марта 2009

Я использую параллельный linq, и я пытаюсь загрузить много URL одновременно, используя, по сути, такой код:

int threads = 10;
Dictionary<string, string> results = urls.AsParallel( threads ).ToDictionary( url => url, url => GetPage( url );

Поскольку загрузка веб-страниц связана с сетью, а не с процессором, использование большего количества потоков, чем мое число процессоров / ядер, очень выгодно, поскольку большую часть времени в каждом потоке тратится на ожидание догонения сети. Однако, судя по тому факту, что выполнение вышеизложенного с потоками = 2 имеет ту же производительность, что и потоки = 10 на моей двухъядерной машине, я думаю, что шаги, отправляемые в AsParallel, ограничены количеством ядер.

Есть ли способ отменить это поведение? Есть ли подобная библиотека, которая не имеет этого ограничения?

(я нашел такую ​​библиотеку для python, но мне нужно что-то, что работает в .Net)

Ответы [ 4 ]

12 голосов
/ 04 марта 2009

URL ссылаются на один и тот же сервер? Если это так, возможно, вы превысили предел HTTP-соединения, а не поток. Есть простой способ сказать - измени свой код на:

int threads = 10;
Dictionary<string, string> results = urls.AsParallel(threads)
    .ToDictionary(url => url, 
                  url => {
                      Console.WriteLine("On thread {0}",
                                        Thread.CurrentThread.ManagedThreadId);
                      return GetPage(url);
                  });

РЕДАКТИРОВАТЬ: Хм. Я не могу заставить ToDictionary() распараллелить на всех с небольшим количеством примера кода. Он отлично работает для Select(url => GetPage(url)), но не ToDictionary. Будем немного искать.

РЕДАКТИРОВАТЬ: Хорошо, я все еще не могу получить ToDictionary для распараллеливания, но вы можете обойти это. Вот короткая, но полная программа:

using System;
using System.Collections.Generic;
using System.Threading;
using System.Linq;
using System.Linq.Parallel;

public class Test
{

    static void Main()
    {
        var urls = Enumerable.Range(0, 100).Select(i => i.ToString());

        int threads = 10;
        Dictionary<string, string> results = urls.AsParallel(threads)
            .Select(url => new { Url=url, Page=GetPage(url) })
            .ToDictionary(x => x.Url, x => x.Page);
    }

    static string GetPage(string x)
    {
        Console.WriteLine("On thread {0} getting {1}",
                          Thread.CurrentThread.ManagedThreadId, x);
        Thread.Sleep(2000);
        return x;
    }
}

Итак, сколько потоков это использует? 5. Почему? Бог знает У меня есть 2 процессора, так что это не так - и мы указали 10 потоков, так что это не так. Он по-прежнему использует 5, даже если я изменю GetPage, чтобы забить процессор.

Если вам нужно использовать это только для одной конкретной задачи - и вы не возражаете против слегка вонючего кода - вам, честно говоря, лучше всего реализовать его самостоятельно.

7 голосов
/ 04 марта 2009

По умолчанию .Net имеет ограничение в 2 одновременных подключения к конечной точке обслуживания (IP: порт). Вот почему вы бы не увидели разницы, если все URL-адреса одного и того же сервера.

Им можно управлять, используя свойство ServicePointManager.DefaultPersistentConnectionLimit .

1 голос
/ 05 марта 2009

Я думаю, что на этот вопрос уже есть хорошие ответы, но я хотел бы сделать одно важное замечание. Использование PLINQ для задач, которые не связаны с процессором, в принципе неверно. Не сказать, что это не будет работать - это будет работать, но использование нескольких потоков, когда это не нужно, может вызвать проблемы.

К сожалению, нет хорошего способа решить эту проблему в C #. В F # вы можете использовать асинхронные рабочие процессы, которые работают параллельно, но не блокировать поток при выполнении асинхронных вызовов (под прикрытием он использует методы BeginOperation и EndOperation). Вы можете найти больше информации здесь:

Эта же идея в некоторой степени может быть использована в C #, но выглядит немного странно (но она более эффективна). Я написал статью об этом, и есть также библиотека, которая должна быть немного более развитой, чем моя первоначальная идея:

0 голосов
/ 04 марта 2009

Мониторинг вашего сетевого трафика. Если URL-адреса принадлежат одному домену, это может ограничивать пропускную способность. Больше подключений может не обеспечить ускорение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...