首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >带缓存的StringBuilder、ThreadStatic

带缓存的StringBuilder、ThreadStatic
EN

Stack Overflow用户
提问于 2017-08-31 04:11:02
回答 3查看 761关注 0票数 0

我偶然发现了来自Writing Large, Responsive .NET Framework Apps的以下代码。

下面的代码使用StringBuilder创建了一个类似SomeType<T1, T2, T3>的字符串,并演示了缓存StringBuilder以提高性能。

代码语言:javascript
复制
 public void Test3()
        {
            Console.WriteLine(GenerateFullTypeName("SomeType", 3));
        }

        // Constructs a name like "SomeType<T1, T2, T3>"  
        public string GenerateFullTypeName(string name, int arity)
        {
            //StringBuilder sb = new StringBuilder();
            StringBuilder sb = AcquireBuilder();

            sb.Append(name);
            if (arity != 0)
            {
                sb.Append("<");
                for (int i = 1; i < arity; i++)
                {
                    sb.Append("T"); sb.Append(i.ToString()); sb.Append(", ");
                }
                sb.Append("T"); sb.Append(arity.ToString()); sb.Append(">");
            }

            //return sb.ToString();
            /* Use sb as before */
            return GetStringAndReleaseBuilder(sb);
        }
        [ThreadStatic]
        private static StringBuilder cachedStringBuilder;

        private static StringBuilder AcquireBuilder()
        {
            StringBuilder result = cachedStringBuilder;
            if (result == null)
            {
                return new StringBuilder();
            }
            result.Clear();
            cachedStringBuilder = null;
            return result;
        }

        private static string GetStringAndReleaseBuilder(StringBuilder sb)
        {
            string result = sb.ToString();
            cachedStringBuilder = sb;
            return result;
        }

但是,下面两个修改后的方法在缓存StringBuilder方面更好,这是正确的吗?只有AcquireBuilder需要知道如何缓存它。

代码语言:javascript
复制
 private static StringBuilder AcquireBuilder()
        {
            StringBuilder result = cachedStringBuilder;
            if (result == null)
            {
                //unlike the method above, assign it to the cache
                cachedStringBuilder = result = new StringBuilder();
                return result;
            }
            result.Clear();
            //no need to null it
           // cachedStringBuilder = null;
            return result;
        }

        private static string GetStringAndReleaseBuilder(StringBuilder sb)
        {
            string result = sb.ToString();
             //other method does not to assign it again.
            //cachedStringBuilder = sb;
            return result;
        }

另一个问题是,原来的方法不是线程安全的,为什么在演示中使用ThreadStatic?

EN

回答 3

Stack Overflow用户

发布于 2017-08-31 04:46:39

焦点放在创建新StringBuilder实例的代码行上。该代码导致在StringBuilder实现中分配sb.ToString()和内部分配,但是如果您想要字符串结果,则无法控制这些分配。

好吧,根据例子,他们忽略了自己的话。更好的方法是缓存并重用它(在使用之前将其清理干净)。除了需要的那些以外,没有分配:

代码语言:javascript
复制
    public static string GenerateFullTypeName(string name, int arity)
    {
        //StringBuilder sb = new StringBuilder();
        StringBuilder sb = cached.Value;
        sb.Clear();

        sb.Append(name);
        if (arity != 0)
        {
            sb.Append("<");
            for (int i = 1; i < arity; i++)
            {
                sb.Append("T"); sb.Append(i.ToString()); sb.Append(", ");
            }
            sb.Append("T"); sb.Append(arity.ToString()); sb.Append(">");
        }

        //return sb.ToString();
        /* Use sb as before */
        return sb.ToString();
    }

    [ThreadStatic]
    private static Lazy<StringBuilder> cached = new Lazy<StringBuilder>(()=> new StringBuilder());

此外,我认为这是GC如何损害您的应用程序性能的一个非常糟糕的例子。时间和短字符串基本上不会进入第二代,很快就会被处理掉。更好的方法是像缓冲器这样的东西,用于WCF传输流,在池中返回这个缓冲区,或者任务一般是如何工作的(同样的想法),并分配它们的肠子,但不是StringBuilder,duh。

票数 0
EN

Stack Overflow用户

发布于 2017-08-31 04:47:07

以下是“原始方法不是线程安全的”的答案

基本上,作者所做的是用ThreadStaticAttribute标记属性,这使得它是线程安全的,因为值只对这个线程有效。不同的线程会有不同的值/引用。并且这个“缓存”将只在线程本身的生命周期内有效。即使方法本身不是线程安全的,它访问的值也是。

我不认为,这通常是一个很好的例子,因为这是什么意思?你保留了一个sting builder的实例,无论如何你都要把它清理掉。

如果您对每个线程的静态值特别感兴趣,那么拥有ThreadStaticAttribute是一件很好的事情。如果您对线程安全的静态方法更感兴趣,请查看lock

代码语言:javascript
复制
private static MyClass _myClass;
private static object _lock = new object();

public static MyClass GetMyClass()
{
    if (_myClass == null)
    {
        lock(_lock)
        {
            if (_myClass == null)
            {
                _myClass = new MyClass();
            }
        }

    }
    return _myClass;
}
票数 0
EN

Stack Overflow用户

发布于 2017-08-31 06:29:37

这个例子只展示了主要思想,并没有深入探讨。让我们用新方法扩展我们的类来添加命名空间。

代码语言:javascript
复制
public string GenerateFullTypeName(string name, int arity, string @namespace)
{
    StringBuilder sb = AcquireBuilder();
    sb.Append(this.GenerateNamespace(@namespace));
    sb.Append(this.GenerateFullTypeName(name, arity));
    return GetStringAndReleaseBuilder(sb);
}

public string GenerateNamespace(string @namespace)
{
    StringBuilder sb = AcquireBuilder();

    sb.Append(@namespace);
    sb.Append(".");

    return GetStringAndReleaseBuilder(sb);
}

测试一下Console.WriteLine(test.GenerateFullTypeName("SomeType", 3, "SomeNamespace"));的原始代码是否按预期工作(输出字符串为SomeNamespace.SomeType<T1, T2, T3>),但是如果我们应用你的“优化”,会发生什么呢?输出字符串将是错误的(SomeType<T1, T2, T3>SomeType<T1, T2, T3>),因为对于该类中的所有方法,即使该实例仍在使用中,我们也只使用StringBuilder的一个(现金)实例。这就是为什么实例仅在使用后才存储在字段中,如果再次使用则从字段中删除。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45968920

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档