【问题标题】:How to search for a string in a large text file, using buffers and multithreading如何使用缓冲区和多线程在大型文本文件中搜索字符串
【发布时间】:2021-04-17 14:17:22
【问题描述】:

我想在一个非常大的文本文件(包含数十 GB 的文本)中查找一个字符串。我需要使用缓冲区和多线程,这就是我想做的:

  1. 在每次迭代时使用缓冲区创建一个循环读取文本块。
  2. 将块拆分为给定数量的线程。
  3. 每个线程都会在它收到的部分文本中搜索字符串。
  4. 如果找到字符串,则打印其位置,否则读取文本文件的另一块。

这是我尝试做的:

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
   

}

现在我在实现这个算法时有两个问题:

  1. 我现在不知道如何同时运行所有线程并在找到结果时停止。
  2. 我只知道如何使用缓冲区而不是文本中的块来逐行读取。

输入数据的约束和不变量:

  1. 我不知道文本文件的确切大小,只是它太大而无法一次读取,但我知道线程的最大缓冲区大小为 10k。
  2. 我正在搜索的字符串大小未知 - 可能是文本文件中的一个单词,它只包含字母和数字,没有空格或换行符。

我是 C# 的新手,所以任何帮助都会很棒。

【问题讨论】:

  • 如果搜索到的文本恰好在两个连续缓冲区的边界处被分成两半怎么办?
  • 指令是使用缓冲区读取文件(因为它太大而无法一次读取)并在 perellal 中使用多个线程来查找字符串
  • 什么是“非常大” - 为可能的大小写一个示例。
  • 线程最大缓冲区大小为10k
  • @Dana你想在帖子中添加新的细节,而不是在cmets中,新读者不一定会在cmets中搜索。

标签: c# multithreading search


【解决方案1】:
  1. 首先,您要对文件读取和缓冲区处理进行基准测试,以了解您是否受 IO 限制、内存限制或 CPU 限制。如果您受 IO 限制,则多线程方法毫无用处。您可以使用 c# 秒表,可以使用 Intel 的 Advisor 和 Vtune 分析器(它适用于任何程序集),您可以使用 c# 分析器(在 Visual Studio 中使用 alt+F2)。

  2. 当您将文件拆分为 n 个内存块/缓冲区时,您也承担了拆分关键字的风险,因此您希望在多线程方法中考虑这一方面,并​​计入工程 + 维护成本。

  3. 您想打开多个filestreams 并且每个流都关联到一个线程。

  4. 在 c# 中,您希望使用比线程更现代的方法,即任务;当您完全找到关键字时,您需要进行线程安全更新(使用锁);一个半伪代码:

    private object _mtx = new();
    private List<long> _foundPositions = new();
    
    public void StartMultireader()
    {
        int ncores = 16; // add manually/from config file/from CPU detection
        for (int i = 0; i < ncores; i++)
            Task.Run(() => FindKeyword(i));
    }
    
    private void FindKeyword(int fileChunk)
    {
        OpenFileStream(fileChunk);
        SearchAndUpdateResult();
    }
    
    private void SearchAndUpdateResult()
    {
        long position = 0;
        //search;
        {
            //if found
            {
                lock (_mtx)
                {
                    _foundPositions.Add(position);
                }
            }
        }
    }
    
    private void OpenFileStream(int fileChunk)
    {
    }
    

【讨论】:

    【解决方案2】:

    这是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));
    

    此解决方案使用File.ReadLines 方法,因此它在searchedText 不包含CR 或LF 字符的假设下工作。否则将无法检测到该字符串。

    上述解决方案不太可能比普通的 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 避免缓冲输入可枚举的元素,而是立即处理每个新接收到的元素,从而优化了内存使用。这在处理批处理时很重要,因为每个批处理在内存方面可能非常大,并且尽快回收此内存比最小化同步开销更重要(这是缓冲查询输入的意图,默认行为)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-27
      • 1970-01-01
      • 1970-01-01
      • 2019-10-24
      • 2023-04-02
      • 2011-06-23
      相关资源
      最近更新 更多