Есть ли способ выполнить метод несколько раз, кроме управления соединениями / потоками? (.СЕТЬ) - PullRequest
2 голосов
/ 27 января 2009
  1. У меня есть метод, который использует соединение (например, метод, который загружает страницу).
  2. Я должен выполнить этот метод несколько раз (например, загрузить 1000 страниц).
  3. Синхронный и последовательный процесс занимает много времени.
  4. У меня ограниченные ресурсы (максимум 8 потоков и / или 50 одновременных подключений)
  5. Я хочу использовать все ресурсы для его ускорения.
  6. Я знаю, что распараллеливание (PLINQ, Parallel Extensions и т. Д.) Может решить проблему, но я уже пробовал это, и этот подход не работает из-за ограниченных ресурсов.
  7. Я не хочу изобретать колесо, которое распараллеливает задачи такого рода при управлении ресурсами, кто-то должен был сделать это раньше и должен предоставить библиотеку / учебник для этого.

Может кто-нибудь помочь?

Обновление Все становится намного сложнее, когда вы начинаете смешивать асинхронные вызовы с распараллеливанием для максимальной производительности. Это реализовано на нескольких загрузчиках, таких как загрузчик Firefox, он получает 2 загрузки одновременно, а когда одна из них завершена, он получает следующий файл и так далее. Может быть, это кажется очень простым для реализации, но когда я реализовал его, у меня были и остаются проблемы с тем, чтобы сделать его универсальным (полезным для WebRequest и DbCommand) и иметь дело с проблемами (например, тайм-ауты)

Bounty Hunters Награда будет предоставлена ​​первой, которая связывает надежную и бесплатную ($$) библиотеку .NET, которая предоставляет простой способ C # для распараллеливания асинхронных задач как HttpWebRequests.BegingetResponse и SqlCommand. BeginExecuteNonQuery. Распараллеливание не должно ждать завершения N задач, чтобы затем запустить следующую N, но оно должно начать новую задачу, как только одна из N начальных завершится. Метод должен был обеспечить обработку тайм-аута.

Ответы [ 11 ]

5 голосов
/ 01 февраля 2009

Можете ли вы дать больше информации, почему Parallel Linq не будет работать?

Моя точка зрения, ваша задача лучше всего подходит для PLinq. Если вы работаете на 8-ядерном компьютере, PLinq разделит на 8 задач и поставит в очередь все оставшиеся задачи.

Вот черновик кода,

PagesToDownload.AsParallel().ForAll(DownloadMethodWithLimitConnections);

Я не понимаю, почему PLinq потребляет ваши ресурсы. Исходя из моего теста, производительность PLinq даже лучше, чем при использовании ThreadPool.

5 голосов
/ 27 января 2009

Просмотрите счетный семафор для соединений. http://en.wikipedia.org/wiki/Semaphore_(programming)

РЕДАКТИРОВАТЬ: Чтобы ответить на ваш комментарий .NET Framework уже есть один. http://msdn.microsoft.com/en-us/library/system.threading.semaphore.aspx

4 голосов
/ 01 февраля 2009

См. CCR . Этот является «правильным» способом сделать это, хотя вы можете найти кривую обучения библиотек немного ...

3 голосов
/ 04 февраля 2009

Вот что я не получаю: вы говорите, максимум 50 соединений, но только 8 потоков. Каждое соединение по определению «занимает» / работает в потоке. Я имею в виду, что вы не используете DMA или любую другую магию, чтобы снять нагрузку с процессора, поэтому для каждой передачи необходим контекст выполнения. Если вы можете запустить 50 асинхронных запросов одновременно, прекрасно, отлично, сделайте это - вы должны иметь возможность запускать их все из одного потока, так как вызов функции асинхронного чтения по сути не занимает много времени. Если вы, например, имеет 8 ядер и хочет убедиться, что для каждой передачи выделено целое ядро ​​(это, вероятно, глупо, но это ваш код, так что ...), вы можете запустить только 8 передач одновременно.

Мое предложение состоит в том, чтобы просто запустить 50 асинхронных запросов внутри блока синхронизации, чтобы они все запускались до того, как вы позволите любому из них завершить (упрощает математику). Затем используйте семафор подсчета, предложенный Джереми, или синхронизированную очередь, предложенную mbeckish, чтобы отслеживать оставшуюся работу. В конце вашего асинхронного обратного вызова запустите следующее соединение (при необходимости). То есть, запустите 50 соединений, затем, когда закончите, используйте обработчик событий «завершено», чтобы запустить следующее, пока вся работа не будет завершена. Для этого не нужно никаких дополнительных библиотек или фреймворков.

3 голосов
/ 27 января 2009

Вы можете использовать класс .NET System.Threading.ThreadPool. Вы можете установить максимальное количество потоков, которые будут активны одновременно, используя ThreadPool.SetMaxThreads().

2 голосов
/ 01 февраля 2009

У Джеффри Рихтера есть библиотека Power Threading Library, которая может вам помочь. Его биток полон образцов и довольно мощный. Я не смог найти быстрый пример с соединениями, но есть множество примеров, которые могут вам помочь в отношении координации нескольких асинхронных операций.

Может быть загружено отсюда , а здесь несколько статей и примеров . Кроме того, эта ссылка содержит подробную статью Джеффри, объясняющую одновременные асинхронные операции.

2 голосов
/ 27 января 2009
  1. Создайте структуру данных для отслеживания того, какие страницы были извлечены, а что еще нужно получить. например очередь

  2. Используя шаблон Очередь Производителя / Потребителя, отправьте 8 пользовательских потоков, чтобы сделать ваши выборки. Таким образом, вы знаете, что никогда не превысите свой предел в 8 потоков.

См. здесь для хорошего примера.

2 голосов
/ 27 января 2009

Я бы настоятельно рекомендовал держаться подальше от пула потоков, за исключением очень коротких задач. Если вы решите использовать семафор, убедитесь, что вы блокируете только в коде, который ставит в очередь рабочие элементы, а не в начале кода рабочего элемента, или вы быстро заблокируете пул потоков, если ваш (максимальное количество семафоров * 2) равно больше макс пула потоков.

На практике вы действительно никогда не сможете безопасно получить блокировку потока пула, и при этом вы не можете безопасно вызывать большинство асинхронных API (или синхронизировать API, такие как HttpWebRequest.GetResponse, поскольку он также выполняет асинхронные операции под своими покрытиями в пуле потоков) .

1 голос
/ 07 февраля 2009

Вы должны взглянуть на асинхронные рабочие процессы F #.

Вы действительно не хотите, чтобы ваш код был параллельным, а асинхронным

асинхронный относится к программам, которые выполнить несколько длительных операций что не нужно блокировать вызов поток, например, доступ к сеть, вызов веб-служб или выполняя любую другую операцию ввода / вывода в вообще

Это очень интересная статья об этой концепции, объясненная с использованием итераторов C #.

Это великая книга о F # и асинхронном программировании.

Кривая обучения очень плохая (много странных вещей: синтаксис F #, тип Async <'a>, монады и т. Д.), Но это ОЧЕНЬ мощный подход и может использоваться в реальной жизни с отличным взаимодействием C #.

Основная идея здесь - продолжение: пока вы ждете каких-то операций ввода / вывода, пусть ваши потоки будут делать что-то еще!

1 голос
/ 06 февраля 2009

Вот как вы это сделаете с библиотекой базовых классов в .net 3.5: Вызов SetMinThreads не является обязательным - посмотрите, что происходит с & без него.

Вы должны обрабатывать тайм-ауты при замене на DoSomethingThatsSlow

public class ThrottledParallelRunnerTest
{
    public static void Main()
    {
        //since the process is just starting up, we need to boost this
        ThreadPool.SetMinThreads(10, 10);

        IEnumerable<string> args = from i in Enumerable.Range(1, 100)
                                   select "task #" + i;
        ThrottledParallelRun(DoSomethingThatsSlow, args, 8);
    }

    public static void DoSomethingThatsSlow(string urlOrWhatever)
    {
        Console.Out.WriteLine("{1}: began {0}", urlOrWhatever, DateTime.Now.Ticks);
        Thread.Sleep(500);
        Console.Out.WriteLine("{1}: ended {0}", urlOrWhatever, DateTime.Now.Ticks);
    }

    private static void ThrottledParallelRun<T>(Action<T> action, IEnumerable<T> args, int maxThreads)
    {
        //this thing looks after the throttling
        Semaphore semaphore = new Semaphore(maxThreads, maxThreads);

        //wrap the action in a try/finally that releases the semaphore
        Action<T> releasingAction = a =>
                                        {
                                            try
                                            {
                                                action(a);
                                            }
                                            finally
                                            {
                                                semaphore.Release();
                                            }
                                        };

        //store all the IAsyncResult - will help prevent method from returning before completion
        List<IAsyncResult> results = new List<IAsyncResult>();
        foreach (T a in args)
        {
            semaphore.WaitOne();
            results.Add(releasingAction.BeginInvoke(a, null, null));
        }

        //now let's make sure everything's returned. Maybe collate exceptions here?
        foreach (IAsyncResult result in results)
        {
            releasingAction.EndInvoke(result);
        }
    }
}
...