2010-04-20 4 views
3

У нас есть ситуация, когда нашему приложению необходимо обрабатывать ряд файлов, а не выполнять эту функцию синхронно, мы хотели бы использовать многопоточность для разделения рабочей нагрузки между разными потоками.C# Многопоточный файл IO (чтение)

Каждый элемент работы:
1. Открыть файл только для чтения
2. Процесс данные в файле
3. Запишите обработанные данные в Словаре

Мы хотели бы выполнить каждый файл работает над новым потоком? Возможно ли это, и нам лучше использовать ThreadPool или создавать новые потоки, имея в виду, что каждый элемент «работы» занимает всего 30 мс, однако его возможно, что нужно будет обрабатывать сотни файлов.

Любые идеи для повышения эффективности.

EDIT: В настоящий момент мы используем ThreadPool для обработки этого. Если у нас есть 500 файлов для обработки, мы циклически просматриваем файлы и выделяем каждый «блок обработки» в threadpool с помощью QueueUserWorkItem.

Пригодится ли использовать пул потоков для этого?

+0

Если требуется, а в синхронной обработке последовательная обработки, то потоковый окончательно неправильный путь. –

+0

процесс, указанный выше (номера 1-3), является полной единицей работы. мы могли бы выполнить сотни таких операций, которые не должны обрабатываться синхронно. – washtik

+3

Это пустая трата времени. Вам нужно больше дисков, а не больше ядер процессора/потоков. –

ответ

2

Я предлагаю вам иметь конечное число потоков (скажем 4), а затем иметь 4 пула работы. То есть Если у вас 400 файлов для обработки, равно 100 файлов на поток разделяются равномерно. Затем вы создаете потоки и передаете каждой своей работе и позволяете им работать, пока они не закончат свою конкретную работу.

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

+0

- это приложение класса семафора? – washtik

+0

Нет, это просто модель нарезки, но краткая. Теория гласит, что в любом случае наличие большего количества потоков, чем ядра вашего процессора, является отходами. Обычно я выбираю 2xCoreCount. Кроме того, ваш жесткий диск, вероятно, будет самой большой бутылочной горловиной, поэтому у вас больше не будет никаких преимуществ. Нет необходимости в threadpool, поскольку у вас есть статический поток, каждый из которых выполняет заданную работу. – Chris

+0

Я не вижу проблемы, просто присваивая все задачи ThreadPool, а затем позволяя определить операцию. Разве он не делает все дросселирование, контроль потока под капотом? – washtik

0

Используйте ThreadPool.QueueUserWorkItem для выполнения каждой самостоятельной задачи. Определенно не создавайте сотни потоков. Это может вызвать серьезные головные боли.

0

Общее правило использования ThreadPool заключается в том, что вы не хотите беспокоиться о том, когда потоки завершатся (или используют Mutexes для их отслеживания) или беспокоятся о прекращении потоков.

Так что вам нужно беспокоиться о том, когда работа будет завершена? Если нет, ThreadPool - лучший вариант. Если вы хотите отслеживать общий прогресс, остановите потоки, тогда ваша собственная коллекция потоков лучше всего.

ThreadPool, как правило, более эффективен, если вы повторно используете потоки. This question даст вам более подробное обсуждение.

Hth

2

Вместо того, чтобы иметь дело с потоками или управлять пулы непосредственно я предлагаю использовать библиотеку более высокого уровня, как Parallel Extensions (PEX):

var filesContent = from file in enumerableOfFilesToProcess 
        select new 
        { 
         File=file, 
         Content=File.ReadAllText(file) 
        }; 

var processedContent = from content in filesContent 
         select new 
         { 
          content.File, 
          ProcessedContent = ProcessContent(content.Content) 
         }; 

var dictionary = processedContent 
      .AsParallel() 
      .ToDictionary(c => c.File); 

PEX будет обрабатывать управление потоками в соответствии с доступными ядрами и нагрузкой, в то время как вы можете сосредоточиться на бизнес-логике под рукой (ничего себе, это звучит как коммерческий!)

PEX является частью .Net Framework 4.0, но задний порт 3.5 также доступен как часть от the Reactive Framework.

+0

это звучит как реклама. i был нацелен на 2.0 рамки, PEX для 3.5? Возможно, мне нужно просто двигаться со временем и начинать разработку для более высокой версии фреймворка! – washtik

+0

См. Мой обновленный ответ. И да, это великие дни, чтобы перейти к последним битам :) –

+0

Думаю, что я потерял «AsParallel», он должен преобразовать коллекцию обработанных элементов. –

1

Я предлагаю использовать CCR (Concurrency and Coordination Runtime), он будет обрабатывать детали низкого уровня детализации для вас.Что касается вашей стратегии, один поток на рабочий элемент может не быть лучшим подходом в зависимости от того, как вы пытаетесь писать в словарь, потому что вы можете создать тяжелое соперничество, поскольку словари не являются потокобезопасными.

Вот некоторые примеры кода с помощью CCR, чередовании будет работать хорошо здесь:

Arbiter.Activate(dispatcherQueue, Arbiter.Interleave(
    new TeardownReceiverGroup(Arbiter.Receive<bool>(
     false, mainPort, new Handler<bool>(Teardown))), 
    new ExclusiveReceiverGroup(Arbiter.Receive<object>(
     true, mainPort, new Handler<object>(WriteData))), 
    new ConcurrentReceiverGroup(Arbiter.Receive<string>(
     true, mainPort, new Handler<string>(ReadAndProcessData))))); 

public void WriteData(object data) 
{ 
    // write data to the dictionary 
    // this code is never executed in parallel so no synchronization code needed 
} 

public void ReadAndProcessData(string s) 
{ 
    // this code gets scheduled to be executed in parallel 
    // CCR take care of the task scheduling for you 
} 

public void Teardown(bool b) 
{ 
    // clean up when all tasks are done 
} 
+0

Это выглядит очень уродливо по сравнению с PEX или просто ThreadPool.QueueUserWorkItem. Во всяком случае, я не знал об этом; спасибо, что поделились этим! (+1) – ShdNx

1

В долгосрочной перспективе, я думаю, вы будете счастливее, если вы управлять своими собственными потоками. Это позволит вам контролировать количество запущенных и упростить отправку отчета о статусе.

  1. Создайте рабочий класс, который выполняет обработку, и дайте ему процедуру обратного вызова, чтобы возвращать результаты и статус.
  2. Для каждого файла создайте экземпляр рабочего и поток для его запуска. Поместите нить в Queue.
  3. Очистить потоки от очереди до максимума, который вы хотите запустить одновременно. По мере того, как каждый поток завершается, нужно получить еще один. Отрегулируйте максимальную и измеренную пропускную способность. Я предпочитаю использовать Dictionary, чтобы удерживать текущие потоки, с помощью их ManagedThreadId.
  4. Чтобы остановиться рано, просто очистите очередь.
  5. Используйте блокировку вокруг коллекций нитей, чтобы сохранить свое здоровье.
8

Я предлагаю вам использовать ThreadPool.QueueUserWorkItem(...), в этом потоки управляются системой и сетью .net. Шансы на то, что вы завязали свой собственный поток, намного выше. Поэтому я бы рекомендовал вам использовать Threadpool, предоставленный .net. Это очень просто в использовании,

ThreadPool.QueueUserWorkItem(new WaitCallback(YourMethod), ParameterToBeUsedByMethod); 

YourMethod(object o){ Your Code here... }

Для получения дополнительного чтения, пожалуйста, пройдите по ссылке http://msdn.microsoft.com/en-us/library/3dasc8as%28VS.80%29.aspx

Надежда, это помогает

0

Использование ThreadPool для каждой отдельной задачи, безусловно, плохое идея. По моему опыту это, как правило, снижает производительность, а не помогает. Первая причина заключается в том, что требуется значительное количество накладных расходов, чтобы выделить задачу для выполнения ThreadPool. По умолчанию каждому приложению назначается собственный ThreadPool, который инициализируется с пропускной способностью ~ 100. Когда вы выполняете 400 операций в параллельном режиме, для заполнения очереди запросами не требуется много времени, и теперь у вас есть ~ 100 потоков, которые конкурируют за циклы CPU. Да, .NET Framework отлично справляется с дросселированием и приоритетом очереди, однако я обнаружил, что ThreadPool лучше всего оставить для длительных операций, которые, вероятно, не будут происходить очень часто (загрузка файла конфигурации или случайных веб-запросов). Использование ThreadPool для запуска нескольких операций в случайном порядке намного эффективнее, чем использование его для одновременного выполнения сразу нескольких запросов. С учетом текущей информации, лучший курс действий будет что-то похожее на это:

  1. Создать System.Threading.Thread (или использовать одну ThreadPool нить) с очередью, что приложение может отправлять запросы

  2. Используйте методы BeginRead и BeginWrite FileStream для выполнения операций ввода-вывода. Это заставит платформу .NET использовать встроенный API для потоковой передачи и выполнить IO (IOCP).

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

В настоящее время я пишу подобное приложение, и использование этого метода является эффективным и быстрым ... Без каких-либо потоков или дросселирования мое приложение использовало только 10-15% CPU, что может быть приемлемым для некоторых операций в зависимости от Тем не менее, эта обработка сделала мой компьютер медленным, как если бы приложение использовало 80% + процессора. Это был доступ к файловой системе. Функции ThreadPool и IOCP не заботятся, если они увязли ПК, поэтому не путайте, они оптимизированы для производительности, даже если эта производительность означает, что ваш жесткий диск визжит, как свинья.

Единственная проблема, с которой я столкнулся, - это использование памяти, которая была немного высокой (50+ мб) во время тестирования фаза с примерно 35 потоками, открытыми сразу. В настоящее время я работаю над решением, аналогичным рекомендации MSDN для SocketAsyncEventArgs, используя пул, позволяющий одновременно работать с числом запросов, что в конечном итоге привело меня к этому сообщению форума.

Надеется, что это помогает кто-то с их принятием решений в будущем :)

Смежные вопросы