c# - 如何使用缓冲区和多线程在大型文本文件中搜索字符串
问题描述
我想在一个非常大的文本文件(包含数十 GB 的文本)中找到一个字符串。我需要使用缓冲区和多线程,这就是我想做的:
- 在每次迭代时使用缓冲区创建一个循环读取文本块。
- 将块拆分为给定数量的线程。
- 每个线程将在它收到的文本部分中搜索字符串。
- 如果找到字符串,则打印其位置,否则读取文本文件的另一块。
这就是我试图做的:
string[] lines = System.IO.File.ReadAllLines(@textfile);
int counter = lines.Length / nThreads;
int start = 0;
int end = counter;
(int,int)[] results = new (int, int)[nThreads];
results.ToList().ForEach(i => Console.WriteLine(i.ToString()));
for (int i = 0; i < nThreads; i++)
{
Thread thread1 = new Thread(() => { results[i] = ThreadSearch.SubarraySearch(StringToSearch, Delta, lines, start, end); });
// ThreadSearch - function that search the string in the array
thread1.Start();
thread1.Join();
}
// at the end i will go through the results array see if any of the threads found something
}
现在我在实现这个算法时有两个问题:
- 我现在不知道如何同时运行所有线程并在找到结果时停止。
- 我只知道如何使用缓冲区而不是文本中的块来逐行读取。
输入数据的约束和不变量:
- 我不知道文本文件的确切大小,只是它太大而无法一次读取,但我知道线程的最大缓冲区大小为 10k。
- 我正在搜索的字符串的大小是未知的——可能是文本文件中的一个单词,它只包含字母和数字,没有空格或换行符。
我是 C# 的新手,所以任何帮助都会很棒。
解决方案
这是一种PLINQ方法。通过使用 PLINQ 库,您可以避免显式线程管理。该库使用线程为您管理ThreadPool
线程。AsParallel
运算符表示后续运算符将并行执行。
string filePath = @"C:\YourFile.txt";
string searchedText = "TheTextToFind";
bool fileContainsText = File
.ReadLines(filePath)
.AsParallel()
.WithDegreeOfParallelism(Environment.ProcessorCount)
.Any(line => line.Contains(searchedText, StringComparison.Ordinal));
此解决方案使用该方法,因此它在不包含 CR 或 LF 字符File.ReadLines
的假设下工作。searchedText
否则不会检测到字符串。
上述解决方案不太可能比普通的 LINQ 查询更快,因为与将每一行传递到不同线程相关的同步开销。一种更复杂的方法是分批处理生产线,例如每批 1000 条生产线。目前没有用于批处理枚举的可用内置运算符,但您可以使用System.Interactive包中的Buffer
运算符:
bool fileContainsText = Partitioner
.Create(File.ReadLines(filePath).Buffer(1000),
EnumerablePartitionerOptions.NoBuffering)
.AsParallel()
.WithDegreeOfParallelism(Environment.ProcessorCount)
.Any(array => array.Any(line =>
line.Contains(searchedText, StringComparison.Ordinal)));
您还可以使用MoreLinq包中的Batch
运算符。
该EnumerablePartitionerOptions.NoBuffering
配置通过指示 PLINQ 避免缓冲输入可枚举的元素,而是立即处理每个新接收到的元素,从而优化了内存使用。这在处理批处理时很重要,因为每个批处理在内存方面可能非常大,并且尽快回收此内存比最小化同步开销更重要(这是缓冲查询输入的意图,默认行为)。
推荐阅读
- angularjs - 如何重用模态作为确认对话框以从 AngularJS 中的控制器中删除操作?
- firebase - 将 Google 电子表格数据导入 Firebase
- javascript - Angular RXJS 返回 3 个可观察流的数组
- azure - Azure App Services API - 域 - 创建或更新 - domain.Properties.Consent 的值无效。域名转移
- elasticsearch - 带日期的 Elasticsearch 无痛脚本
- ios - 如何检测电子邮件地址更改是否已恢复?
- tesseract - 无法识别的 Quartz MS 字体
- spring - 使用 @WebMvcTest 测试控制器时处理 Spring Security 配置
- javascript - React ComponentDidMount 中的状态是否发生变化?
- django - 如何修复错误:在 Elastic Beanstalk 上永久找不到 pg_config 可执行文件