草庐IT

c# - .NET Core 2.0 正则表达式超时死锁

coder 2024-05-31 原文

我有一个 .NET Core 2.0 应用程序,我在其中迭代了许多不同大小(总共 220GB)的文件(600,000 个)。

我用

枚举它们
new DirectoryInfo(TargetPath)
    .EnumerateFiles("*.*", SearchOption.AllDirectories)
    .GetEnumerator()

并使用

迭代它们
Parallel.ForEach(contentList.GetConsumingEnumerable(),
    new ParallelOptions
    {
        MaxDegreeOfParallelism = Environment.ProcessorCount * 2
    },
    file => ...

在其中,我有一个正则表达式列表,然后我使用它扫描文件

Parallel.ForEach(_Rules,
    new ParallelOptions
    {
        MaxDegreeOfParallelism = Environment.ProcessorCount * 2
    },
    rule => ... 

最后,我使用 Regex 类的实例进行匹配

RegEx = new Regex(
    Pattern.ToLowerInvariant(),
    RegexOptions.Multiline | RegexOptions.Compiled,
    TimeSpan.FromSeconds(_MaxSearchTime))

这个实例在所有文件中共享,所以我编译它一次。有 175 种模式应用于文件。

在随机(ish)点,应用程序死锁并且完全没有响应。再多的 try/catch 也无法阻止这种情况的发生。如果我使用完全相同的代码并为 .NET Framework 4.6 编译它,它可以毫无问题地工作。

我已经尝试了很多东西,我目前的测试似乎有效(但我非常谨慎!)是不使用实例,而是调用 STATIC Regex.Matches 方法每次。我不知道我在性能上受到了多大的打击,但至少我没有陷入僵局。

我可以使用一些见解或至少作为一个警示故事。

更新: 我得到这样的文件列表:

private void GetFiles(string TargetPath, BlockingCollection<FileInfo> ContentCollector)
    {
        List<FileInfo> results = new List<FileInfo>();
        IEnumerator<FileInfo> fileEnum = null;
        FileInfo file = null;
        fileEnum = new DirectoryInfo(TargetPath).EnumerateFiles("*.*", SearchOption.AllDirectories).GetEnumerator();
        while (fileEnum.MoveNext())
        {
            try
            {
                file = fileEnum.Current;
                //Skip long file names to mimic .Net Framework deficiencies
                if (file.FullName.Length > 256) continue;
                ContentCollector.Add(file);
            }
            catch { }
        }
        ContentCollector.CompleteAdding();
    }

在我的规则类中,以下是相关方法:

_RegEx = new Regex(Pattern.ToLowerInvariant(), RegexOptions.Multiline | RegexOptions.Compiled, TimeSpan.FromSeconds(_MaxSearchTime));
...
    public MatchCollection Matches(string Input) { try { return _RegEx.Matches(Input); } catch { return null; } }
    public MatchCollection Matches2(string Input) { try { return Regex.Matches(Input, Pattern.ToLowerInvariant(), RegexOptions.Multiline, TimeSpan.FromSeconds(_MaxSearchTime)); } catch { return null; } }

那么,这里是匹配代码:

    public List<SearchResult> GetMatches(string TargetPath)
    {
        //Set up the concurrent containers
        ConcurrentBag<SearchResult> results = new ConcurrentBag<SearchResult>();
        BlockingCollection<FileInfo> contentList = new BlockingCollection<FileInfo>();

        //Start getting the file list
        Task collector = Task.Run(() => { GetFiles(TargetPath, contentList); });
        int cnt = 0;
        //Start processing the files.
        Task matcher = Task.Run(() =>
        {
            //Process each file making it as parallel as possible                
            Parallel.ForEach(contentList.GetConsumingEnumerable(), new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }, file =>
            {
                //Read in the whole file and make it lowercase
                //This makes it so the Regex engine does not have
                //to do it for each 175 patterns!
                StreamReader stream = new StreamReader(File.OpenRead(file.FullName));
                string inputString = stream.ReadToEnd();
                stream.Close();
                string inputStringLC = inputString.ToLowerInvariant();

                //Run through all the patterns as parallel as possible
                Parallel.ForEach(_Rules, new ParallelOptions { MaxDegreeOfParallelism = Environment.ProcessorCount * 2 }, rule =>
                {
                    MatchCollection matches = null;
                    int matchCount = 0;
                    Stopwatch ruleTimer = Stopwatch.StartNew();

                    //Run the match for the rule and then get our count (does the actual match iteration)
                    try
                    {
                        //This does not work - Causes Deadlocks:
                        //matches = rule.Matches(inputStringLC);

                        //This works - No Deadlocks;
                        matches = rule.Matches2(inputStringLC);

                        //Process the regex by calling .Count()
                        if (matches == null) matchCount = 0;
                        else matchCount = matches.Count;
                    }

                    //Catch timeouts
                    catch (Exception ex)
                    {
                        //Log the error
                        string timeoutMessage = String.Format("****** Regex Timeout: {0} ===> {1} ===> {2}", ruleTimer.Elapsed, rule.Pattern, file.FullName);
                        Console.WriteLine(timeoutMessage);
                        matchCount = 0;
                    }
                    ruleTimer.Stop();

                    if (matchCount > 0)
                    {
                        cnt++;
                        //Iterate the matches and generate our match records
                        foreach (Match match in matches)
                        {
                            //Fill my result object
                            //...

                            //Add the result to the collection
                            results.Add(result);
                        }
                    }
                });
            });
        });

        //Wait until all are done.
        Task.WaitAll(collector, matcher);

        Console.WriteLine("Found {0:n0} files with {1:n0} matches", cnt, results.Count);


        return results.ToList();
    }

更新 2 我跑的测试没有死锁,但是接近尾声的时候好像卡顿了,不过还是可以用VS打入进程。然后我意识到我没有在我的测试中设置超时,而我在我发布的代码中设置了超时(rule.Matchesrule.Matches2)。 WITH超时,它会死锁。 WITHOUT 超时,它不会。两者仍然适用于 .Net Framework 4.6。我需要正则表达式超时,因为有一些大文件导致某些模式停止。

更新 3: 我一直在研究超时值,它似乎是线程运行、超时异常和导致 Regex 引擎死锁的超时值的某种组合。我无法准确确定,但超时 >= 5 分钟似乎有帮助。作为临时修复,我可能会将值设置为 10 分钟,但这不是永久修复!

最佳答案

如果我猜的话,我会责怪正则表达式

  • RegexOptions.Compiled 未在 .NET Core 2.0 ( source ) 中实现
  • 您的 175 个模式中的一些可能是 slightly evil

这可能会导致 .NET Framework 4.6 和 .NET Core 2.0 之间出现显着的性能差异,从而导致应用程序无响应。

关于c# - .NET Core 2.0 正则表达式超时死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50356774/

有关c# - .NET Core 2.0 正则表达式超时死锁的更多相关文章

  1. ruby 正则表达式 - 如何替换字符串中匹配项的第 n 个实例 - 2

    在我的应用程序中,我需要能够找到所有数字子字符串,然后扫描每个子字符串,找到第一个匹配范围(例如5到15之间)的子字符串,并将该实例替换为另一个字符串“X”。我的测试字符串s="1foo100bar10gee1"我的初始模式是1个或多个数字的任何字符串,例如,re=Regexp.new(/\d+/)matches=s.scan(re)给出["1","100","10","1"]如果我想用“X”替换第N个匹配项,并且只替换第N个匹配项,我该怎么做?例如,如果我想替换第三个匹配项“10”(匹配项[2]),我不能只说s[matches[2]]="X"因为它做了两次替换“1fooX0barXg

  2. ruby - 简单获取法拉第超时 - 2

    有没有办法在这个简单的get方法中添加超时选项?我正在使用法拉第3.3。Faraday.get(url)四处寻找,我只能先发起连接后应用超时选项,然后应用超时选项。或者有什么简单的方法?这就是我现在正在做的:conn=Faraday.newresponse=conn.getdo|req|req.urlurlreq.options.timeout=2#2secondsend 最佳答案 试试这个:conn=Faraday.newdo|conn|conn.options.timeout=20endresponse=conn.get(url

  3. c# - 如何在 ruby​​ 中调用 C# dll? - 2

    如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

  4. C# 到 Ruby sha1 base64 编码 - 2

    我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

  5. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  6. ruby - 正则表达式将非英文字母匹配为非单词字符 - 2

    @raw_array[i]=~/[\W]/非常简单的正则表达式。当我用一些非拉丁字母(具体来说是俄语)尝试时,条件是错误的。我能用它做什么? 最佳答案 @raw_array[i]=~/[\p{L}]/使用西里尔字符进行测试。引用:http://www.regular-expressions.info/unicode.html#prop 关于ruby-正则表达式将非英文字母匹配为非单词字符,我们在StackOverflow上找到一个类似的问题: https://

  7. ruby-on-rails - Rails 优雅地处理超时 session ? - 2

    使用rails4,ruby2。我在rails配置中为我的cookiesession设置了30分钟的超时时间。问题是,如果我转到表单,让session超时,然后提交表单,我会收到此ActionController::InvalidAuthenticityToken错误。如何在Rails中优雅地处理这个错误?比如说,重定向到登录屏幕? 最佳答案 在您的ApplicationController:rescue_fromActionController::InvalidAuthenticityTokendoredirect_tosome_p

  8. ruby - 正则表达式在哪个位置失败? - 2

    我需要一个非常简单的字符串验证器来显示第一个符号与所需格式不对应的位置。我想使用正则表达式,但在这种情况下,我必须找到与表达式相对应的字符串停止的位置,但我找不到可以做到这一点的方法。(这一定是一种相当简单的方法……也许没有?)例如,如果我有正则表达式:/^Q+E+R+$/带字符串:"QQQQEEE2ER"期望的结果应该是7 最佳答案 一个想法:你可以做的是标记你的模式并用可选的嵌套捕获组编写它:^(Q+(E+(R+($)?)?)?)?然后你只需要计算你获得的捕获组的数量就可以知道正则表达式引擎在模式中停止的位置,你可以确定匹配结束

  9. ruby - 有没有办法从 ruby​​ case 语句中访问表达式? - 2

    我想从then子句中访问c​​ase语句表达式,即food="cheese"casefoodwhen"dip"then"carrotsticks"when"cheese"then"#{expr}crackers"else"mayo"end在这种情况下,expr是食物的当前值(value)。在这种情况下,我知道,我可以简单地访问变量food,但是在某些情况下,该值可能无法再访问(array.shift等)。除了将expr移出到局部变量然后访问它之外,是否有直接访问caseexpr值的方法?罗亚附注我知道这个具体示例很简单,只是一个示例场景。 最佳答案

  10. Ruby 在 n *milli* 秒后超时一段代码 - 2

    在Ruby中,我需要在n毫秒秒后暂停一段代码的执行。我知道RubyTimeout库支持秒的超时:http://ruby-doc.org/stdlib/libdoc/timeout/rdoc/index.html这可能吗? 最佳答案 只需为超时使用十进制值。n毫秒的示例:Timeout::timeout(n/1000.0){sleep(100)} 关于Ruby在n*milli*秒后超时一段代码,我们在StackOverflow上找到一个类似的问题: https:

随机推荐