Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/321.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# AsyncLocal中明显的内存泄漏<&燃气轮机;_C#_Asynchronous_Garbage Collection - Fatal编程技术网

C# AsyncLocal中明显的内存泄漏<&燃气轮机;

C# AsyncLocal中明显的内存泄漏<&燃气轮机;,c#,asynchronous,garbage-collection,C#,Asynchronous,Garbage Collection,我有一个庞大的CI构建测试套件,超过3000个测试。为了并行运行测试,我自由地使用了AsyncLocal对象。问题是,自从我开始这么做以来,CI构建机器人已经完成了大约75%,然后因为内存不足而崩溃。除了新引入的AsyncLocal对象之外,没有其他罪魁祸首可以指指,这些对象被用来存储从布尔到复杂对象的任何东西 我想我在某种程度上滥用了系统,有一种“正确”的方法可以使用AsyncLocal,这样在当前上下文终止时,它的值就会被垃圾收集 在我发现asynchlocal的存在之前,我试图用自己的类P

我有一个庞大的CI构建测试套件,超过3000个测试。为了并行运行测试,我自由地使用了
AsyncLocal
对象。问题是,自从我开始这么做以来,CI构建机器人已经完成了大约75%,然后因为内存不足而崩溃。除了新引入的
AsyncLocal
对象之外,没有其他罪魁祸首可以指指,这些对象被用来存储从布尔到复杂对象的任何东西

我想我在某种程度上滥用了系统,有一种“正确”的方法可以使用
AsyncLocal
,这样在当前上下文终止时,它的值就会被垃圾收集

在我发现
asynchlocal
的存在之前,我试图用自己的类
Parallelizable
实现这个功能,这可能会有所帮助。使用NUnit的
TestExecutionContext.CurrentContext.CurrentTest.FullName
,这是可行的,但速度非常慢,然后我深入底层代码,发现它使用的是
AsyncLocal
,所以我决定删掉我的实现,将
可并行化
更改为
AsyncLocal
的包装器,这样我就可以对我现有的测试进行最小程度的暴力。就在那时,我开始摆脱内存故障

确保这些值为GC’d的正确方法是什么

这是我的可并行化代码,FWIW:

    public class Parallelizable<T>
    {
        private readonly AsyncLocal<T> _asyncLocal = new AsyncLocal<T>();
        private readonly AsyncLocal<bool> _valueSet = new AsyncLocal<bool>();
        private readonly T _defaultValue;
        private readonly Func<T> _defaultFunc;

        public Parallelizable()
        {
            _valueSet.Value = false;
        }

        public Parallelizable(T defaultValue)
        {
            _defaultValue = defaultValue;
        }

        public Parallelizable(Func<T> defaultFunc)
        {
            _defaultFunc = defaultFunc;
        }

        public T Value
        {
            get
            {
                if (!_valueSet.Value)
                {
                    _asyncLocal.Value = _defaultFunc != null ? _defaultFunc.Invoke() : _defaultValue;
                    _valueSet.Value = true;
                }

                return _asyncLocal.Value;
            }
            set
            {
                _asyncLocal.Value = value;
                _valueSet.Value = true;
            }
        }
    }
公共类可并行化
{
私有只读AsyncLocal_AsyncLocal=new AsyncLocal();
私有只读AsyncLocal_valueSet=new AsyncLocal();
私有只读T_defaultValue;
私有只读函数_defaultFunc;
公共可并行化()
{
_valueSet.Value=false;
}
公共可并行化(T默认值)
{
_defaultValue=defaultValue;
}
公共可并行化(Func defaultFunc)
{
_defaultFunc=defaultFunc;
}
公共价值
{
得到
{
如果(!\u valueSet.Value)
{
_asyncLocal.Value=\u defaultFunc!=null?\u defaultFunc.Invoke():\u defaultValue;
_valueSet.Value=true;
}
返回_asynclical.Value;
}
设置
{
_asyncLocal.Value=值;
_valueSet.Value=true;
}
}
}
更新:我解决了这个问题,在我的测试夹具基类中找到了对大型对象使用最多的
AsyncLocal
,在
TearDown()
方法中确保处置
AsyncLocal
属性并将其值设置为
null
。这解决了我的问题


然而,问题仍然悬而未决:为什么当
AsyncLocal
对象本身是GC'd时,它们的所有值也没有得到GC'd?这似乎是AsyncLocal实现中的一个bug,不是吗?

您使用的是什么测试框架?我的意思是,某些框架,如MSTest或VSTest,正在新的测试类实例中运行每个测试方法,因此所有非静态成员对于测试方法都是私有的,您可以安全地并行运行测试,而无需其他技巧。@Artur:build bot正在运行
dotnet test
。但是,框架用于编写测试的是什么呢?MSTest?许妮特?努尼特?还有别的吗?如果您不知道,请查看上面定义的属性测试方法并检查它们的名称空间
dotnet测试
对测试框架是不可知的,可以运行任何测试框架。@Artur-Oh,NUnit。(我在问题中提到了NUnit:-))AsyncLocal是围绕ExecutionContext的一个方便的包装器,它实际上不是数据存储的地方,实际上也不是一次性的。ExecutionContext也是写时复制,因此原始实例对指针的副本一无所知。我倾向于使类结合IDisposable(例如SomeDisposableContext)使用AsyncLocal,并确保在Dispose方法中弹出堆栈/取消设置AsyncLocal的值。通过这种方式,AsyncLocal值可以通过一个简单易读的using语句(非常类似于TransactionScope的使用方式)得到干净的处理?我的意思是,某些框架,如MSTest或VSTest,正在新的测试类实例中运行每个测试方法,因此所有非静态成员对于测试方法都是私有的,您可以安全地并行运行测试,而无需其他技巧。@Artur:build bot正在运行
dotnet test
。但是,框架用于编写测试的是什么呢?MSTest?许妮特?努尼特?还有别的吗?如果您不知道,请查看上面定义的属性测试方法并检查它们的名称空间
dotnet测试
对测试框架是不可知的,可以运行任何测试框架。@Artur-Oh,NUnit。(我在问题中提到了NUnit:-))AsyncLocal是围绕ExecutionContext的一个方便的包装器,它实际上不是数据存储的地方,实际上也不是一次性的。ExecutionContext也是写时复制,因此原始实例对指针的副本一无所知。我倾向于使类结合IDisposable(例如SomeDisposableContext)使用AsyncLocal,并确保在Dispose方法中弹出堆栈/取消设置AsyncLocal的值。通过这种方式,AsyncLocal值通过一个简单易读的using语句(非常类似于TransactionScope的使用方式)被干净地处理。