Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何使用缓冲区和多线程在大型文本文件中搜索字符串_C#_Multithreading_Search - Fatal编程技术网

C# 如何使用缓冲区和多线程在大型文本文件中搜索字符串

C# 如何使用缓冲区和多线程在大型文本文件中搜索字符串,c#,multithreading,search,C#,Multithreading,Search,我想在一个非常大的文本文件(包含数十GB的文本)中找到一个字符串。我需要使用缓冲区和多线程,这就是我想做的: 在每次迭代中使用缓冲区创建一个循环读取文本块 将区块分割为给定数量的线程 每个线程将在收到的文本部分搜索字符串 如果找到字符串,请打印其位置,否则请读取文本文件的另一块 这就是我试图做的: string[] lines = System.IO.File.ReadAllLines(@textfile); int counter = lines.Length / nT

我想在一个非常大的文本文件(包含数十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
       
    
    }
    
    string[]lines=System.IO.File.ReadAllLines(@textfile);
    int计数器=行数。长度/n读取数;
    int start=0;
    int end=计数器;
    (int,int)[]结果=新的(int,int)[n读取];
    results.ToList().ForEach(i=>Console.WriteLine(i.ToString());
    对于(int i=0;i{results[i]=ThreadSearch.subraysearch(StringToSearch,Delta,lines,start,end);});
    //ThreadSearch—在数组中搜索字符串的函数
    thread1.Start();
    thread1.Join();
    }
    //最后,我将遍历results数组,看看是否有线程发现了什么
    }
    
    现在我在实现该算法时遇到两个问题:

  • 我现在不知道如何同时运行所有线程并在找到结果时停止
  • 我只知道如何使用缓冲区逐行读取,而不知道如何从文本中读取一个块
  • 输入数据的约束和不变量:

  • 我不知道文本文件的确切大小,只是它太大,无法一次读取,但我知道线程的最大缓冲区大小是10k
  • 我正在搜索的字符串大小未知-可能是文本文件中的一个单词,它将只包含字母和数字,没有空格或新行
  • 我是C#的新手,所以任何帮助都会很好

  • 首先,您需要对文件读取和缓冲区处理进行基准测试,以了解是IO绑定、内存绑定还是CPU绑定。如果是IO绑定的,那么多线程方法是无用的。您可以使用c#秒表,可以使用Intel的Advisor和Vtune分析器(它适用于任何程序集),也可以使用c#分析器(visual studio中的alt+F2)

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

  • 您希望打开多个流,每个流与一个线程关联

  • 在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)
    {
    }
    
    私有对象_mtx=new();
    私有列表_foundPositions=new();
    public void StartMultireader()
    {
    int ncores=16;//手动添加/来自配置文件/来自CPU检测
    for(int i=0;iFindKeyword(i));
    }
    私有void FindKeyword(int fileChunk)
    {
    OpenFileStream(fileChunk);
    SearchAndUpdateResult();
    }
    私有void SearchAndUpdateResult()
    {
    长位置=0;
    //搜索;
    {
    //如果找到
    {
    锁(mtx)
    {
    _创建位置。添加(位置);
    }
    }
    }
    }
    私有void OpenFileStream(int fileChunk)
    {
    }
    
  • 这里有一个方法。通过使用PLINQ库,可以避免显式线程管理。库使用线程为您管理线程。运算符表示后续运算符将并行执行

    string filePath = @"C:\YourFile.txt";
    string searchedText = "TheTextToFind";
    
    bool fileContainsText = File
        .ReadLines(filePath)
        .AsParallel()
        .WithDegreeOfParallelism(Environment.ProcessorCount)
        .Any(line => line.Contains(searchedText, StringComparison.Ordinal));
    
    此解决方案使用该方法,因此它在假定
    searchedText
    不包含CR或LF字符的情况下工作。否则将不会检测到该字符串

    上述解决方案不太可能比普通LINQ查询更快,因为将每一行传递给不同线程会带来同步开销。一种更复杂的方法是分批处理生产线,比如说,每批处理1000条生产线。目前没有用于批处理可枚举项的内置运算符,但您可以使用包中的
    Buffer
    运算符:

    您还可以使用软件包中的
    批处理
    运算符


    EnumerablePartitionOptions.NoBuffering
    配置通过指示PLINQ避免缓冲输入可枚举元素,而是立即处理每个新接收的元素,从而优化内存使用。这在处理批处理时很重要,因为每个批处理的内存都非常大,尽快回收内存比最小化同步开销更重要(这是缓冲查询输入的目的,默认行为).

    如果搜索的文本恰好在两个连续缓冲区之间的边界处被一分为二,该怎么办?指令是使用缓冲区读取文件(因为文件太大,无法一次读取),并在perellal中使用多个线程来查找“非常大”的字符串-为可能的大小编写一个示例。线程的最大缓冲区大小is10k@Dana如果你想在文章中添加新的细节,而不是在评论中,新读者不一定会在评论中搜索。
    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)));