6

鉴于 MSDN 的以下内容:

正则表达式对象可以在任何线程上创建并在线程之间共享。

我发现为了性能,使用类时最好不要在线程之间共享Regex实例。ThreadLocal

请有人解释一下为什么线程本地实例的运行速度大约快 5 倍?

以下是结果(在 8 核机器上):

   Using Regex singleton' returns 3000000 and takes 00:00:01.1005695
   Using thread local Regex' returns 3000000 and takes 00:00:00.2243880

源代码:

using System;
using System.Linq;
using System.Threading;
using System.Text.RegularExpressions;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static readonly string str = new string('a', 400);
        static readonly Regex re = new Regex("(a{200})(a{200})", RegexOptions.Compiled);

        static void Test(Func<Regex> regexGettingMethod, string methodDesciption)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            var sum = Enumerable.Repeat(str, 1000000).AsParallel().Select(s => regexGettingMethod().Match(s).Groups.Count).Sum();
            sw.Stop();
            Console.WriteLine("'{0}' returns {1} and takes {2}", methodDesciption, sum, sw.Elapsed);
        }

        static void Main(string[] args)
        {
            Test(() => re, "Using Regex singleton");

            var threadLocalRe = new ThreadLocal<Regex>(() => new Regex(re.ToString(), RegexOptions.Compiled));
            Test(() => threadLocalRe.Value, "Using thread local Regex");

            Console.Write("Press any key");
            Console.ReadKey();
        }
    }
}
4

1 回答 1

5

发布我的调查结果。

让我们 ILSpy 正则表达式。它包含对 RegexRunner 的引用。当 Regex 对象匹配某些内容时,它会锁定其 RegexRunner。如果对同一个 Regex 对象有另一个并发请求,则会创建另一个 RegexRunner 临时实例。RegexRunner 很昂贵。共享 Regex 对象的线程越多,浪费时间创建临时 RegexRunner 的机会就越大。希望微软能解决这个问题,以应对大规模并行化时代。

另一件事:当相同的模式在不同的线程中匹配时,Regex 类的静态成员将模式字符串作为参数(如 Match.IsMatch(input, pattern))也必须表现不佳。Regex 维护 RegexRunner 的缓存。两个具有相同模式的并发 Match.IsMatch() 将尝试使用相同的 RegexRunner,并且一个线程将不得不创建临时 RegexRunner。

感谢 Will 让我知道您如何处理主题启动者已找到答案的问题。

于 2011-09-30T16:04:55.560 回答