У меня есть большой файл txt со 100000 строк. Мне нужно запустить n-count потоков и дать каждому потоку uniq строку из этого файла. Каков наилучший способ сделать это? Я думаю, мне нужно прочитать файл по строкам, и итератор должен быть глобальным, чтобы заблокировать его. Загрузка txt файла в список будет трудоемкой, и я могу получить исключение OutofMemory. Есть идеи? Справка plz с некоторым кодом.
Прочитать большой файл txt многопоточным?
Ответ 1
Вы можете использовать File.ReadLines Method, чтобы читать файл по очереди, не загружая сразу весь файл в память, и Parallel.ForEach Method для параллельной обработки строк в нескольких потоках:
Parallel.ForEach(File.ReadLines("file.txt"), (line, _, lineNumber) =>
{
// your code here
});
Ответ 2
После выполнения моих собственных тестов для загрузки 61,277,203 строк в память и сдвига значений в словарь /ConcurrentDictionary () результаты, похоже, поддерживают ответ @dtb выше, что использование следующего подхода является самым быстрым:
Parallel.ForEach(File.ReadLines(catalogPath), line =>
{
});
Мои тесты также показали следующее:
- File.ReadAllLines() и File.ReadAllLines(). Кажется, что AsParallel() работает почти с одинаковой скоростью в файле такого размера. Глядя на активность моего процессора, кажется, что оба они, похоже, используют два из моих 8 ядер?
- Чтение всех данных сначала с использованием File.ReadAllLines() выглядит намного медленнее, чем использование File.ReadLines() в цикле Parallel.ForEach().
- Я также попробовал шаблон производителя/потребителя или MapReduce, где для чтения данных использовался один поток, а для его обработки использовался второй поток. Это также, похоже, не превосходит простой шаблон выше.
Я включил пример этого шаблона для справки, так как он не указан на этой странице:
var inputLines = new BlockingCollection<string>();
ConcurrentDictionary<int, int> catalog = new ConcurrentDictionary<int, int>();
var readLines = Task.Factory.StartNew(() =>
{
foreach (var line in File.ReadLines(catalogPath))
inputLines.Add(line);
inputLines.CompleteAdding();
});
var processLines = Task.Factory.StartNew(() =>
{
Parallel.ForEach(inputLines.GetConsumingEnumerable(), line =>
{
string[] lineFields = line.Split('\t');
int genomicId = int.Parse(lineFields[3]);
int taxId = int.Parse(lineFields[0]);
catalog.TryAdd(genomicId, taxId);
});
});
Task.WaitAll(readLines, processLines);
Вот мои тесты:
Я подозреваю, что при определенных условиях обработки шаблон производителя/потребителя может превзойти простой шаблон Parallel.ForEach(File.ReadLines()). Однако в этой ситуации этого не произошло.
Ответ 3
Прочитайте файл в одном потоке, добавив его строки в блокирующую очередь. Запустите N
задачи чтения из этой очереди. Установите максимальный размер очереди, чтобы предотвратить ошибки в памяти.
Ответ 4
Что-то вроде:
public class ParallelReadExample
{
public static IEnumerable LineGenerator(StreamReader sr)
{
while ((line = sr.ReadLine()) != null)
{
yield return line;
}
}
static void Main()
{
// Display powers of 2 up to the exponent 8:
StreamReader sr = new StreamReader("yourfile.txt")
Parallel.ForEach(LineGenerator(sr), currentLine =>
{
// Do your thing with currentLine here...
} //close lambda expression
);
sr.Close();
}
}
Думаю, это сработает. (Нет компилятора С#/IDE здесь)
Ответ 5
Как упоминалось выше @dtb, самый быстрый способ прочитать файл, а затем обработать отдельные строки в файле: 1) сделать File.ReadAllLines() в массив 2) Используйте цикл Parallel.For для итерации по массиву.
Здесь вы можете прочитать больше тестов производительности.
Основной смысл кода, который вам нужно написать:
string[] AllLines = File.ReadAllLines(fileName);
Parallel.For(0, AllLines.Length, x =>
{
DoStuff(AllLines[x]);
//whatever you need to do
});
С введением больших размеров массива в .Net4, если у вас много памяти, это не должно быть проблемой.
Ответ 6
Если вы хотите ограничить количество потоков на n
, проще всего использовать AsParallel()
вместе с WithDegreeOfParallelism(n)
, чтобы ограничить количество потоков:
string filename = "C:\\TEST\\TEST.DATA";
int n = 5;
foreach (var line in File.ReadLines(filename).AsParallel().WithDegreeOfParallelism(n))
{
// Process line.
}