我在 Office 的 Open-XML-SDK 库里面找到有代码线程不安全,代码里面使用了 TryGetValue 加 TryAdd 的方法添加对象,而线程安全的方法是通过 GetOrAdd 方法。不过在小伙伴的评论我找到了 GetOrAdd 性能其实在有闭包的时候不如使用 TryGetValue 加 TryAdd 调用这两个方法,但是 GetOrAdd 的优势在于能做到只创建一次对象
在 Avoid multi-thread creates ElementMetadata object by lindexi · Pull Request #758 · OfficeDev/Open-XML-SDK 我找到了 OpenXML SDK 的代码存在线程不安全,代码如下
也就是调用 Create 多线程调用将会创建多个不同的实例,如果修改为 GetOrAdd 方法,那么只会创建一个对象实例
但是如果在对象创建的时间可以忽略的前提下,如 CreateInternal 方法的耗时可以忽略,同时在 OpenXML 的这个业务里面,其实多创建对象是没有问题的,那么此时使用 TryGetValue 加上 TryAdd 的方法的性能会比使用 GetOrAdd 的性能高
这是我更改的方法,使用 GetOrAdd 可以做到只创建一个对象
此时做性能测试对比,性能测试的代码放在本文最后
可以看到使用 Create 方法的性能更好,同时申请的对象也更少
Method | Mean | Error | StdDev | Gen 0 | Gen 1 | Gen 2 | Allocated |
---|---|---|---|---|---|---|---|
Create | 22.19 ns | 0.154 ns | 0.144 ns | - | - | - | - |
Create2 | 37.22 ns | 0.337 ns | 0.315 ns | 0.0210 | - | - | 88 B |
为什么 Create2 方法会更慢,同时需要申请内存?原因是调用
每次使用 GetOrAdd 方法都需要创建一个 Lambda 表达式和传入参数,需要创建类,所以性能上不如原先代码
那么如果没有闭包呢?
接下来我测试了值存在和不存在等的比较,测试效果如下 GetOrAdd 需要传入一个 Lambda 表达式,这个表达式需要传入一个 element 变量,这将需要创建一个闭包
Method | Mean | Error | StdDev | Median |
---|---|---|---|---|
GetOrAddExistWithClosed | 1.702 μs | 0.0339 μs | 0.0772 μs | 1.659 μs |
GetOrAddExistWithValue | 1.586 μs | 0.0460 μs | 0.1335 μs | 1.518 μs |
GetOrAddNotExistWithClosed | 1.422 μs | 0.0181 μs | 0.0141 μs | 1.417 μs |
GetOrAddNotExistWithValue | 1.591 μs | 0.0665 μs | 0.1940 μs | 1.529 μs |
GetOrAddExistWithoutClosed | 1.986 μs | 0.0204 μs | 0.0180 μs | 1.991 μs |
GetOrAddNotExistWithoutClosed | 2.054 μs | 0.0167 μs | 0.0130 μs | 2.057 μs |
TryGetValueExist | 1.149 μs | 0.0132 μs | 0.0117 μs | 1.144 μs |
TryGetValueNotExist | 1.281 μs | 0.0353 μs | 0.1019 μs | 1.229 μs |
这里的 TryGetValueNotExist 就是使用 TryGetValue 判断之后再使用 TryAdd 加回去。同时每个 Key 都是不存在的,代码如下
而 GetOrAddExistWithClosed 就是使用 GetOrAdd 方法,同时 Key 是存在的,也就是每次获取的都是存在的相同的值。而 Closed 表示闭包,也就是存在一次闭包的委托创建,代码如下
在 GetOrAdd 还有重载的方法,可以传入需要的参数,也就是 GetOrAddExistWithValue 方法,此时没有传入闭包,而是传入参数
同时测试了不传入闭包,也就是使用类的方法,代码如下
上面是测试 _concurrentDictionary
存在值的,因为在初始化给了 -1
的值,也就是每次获取都是存在值的
如果每次都是 Key 不存在的,也测试了性能就是对应的 NotExist
方法
上面测试的代码放在 github 欢迎小伙伴访问
这是在 OpenXML 的性能测试代码
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。 欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。