Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/.net/20.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# 为什么我这里有锁?_C#_.net_Multithreading_Performance_Locking - Fatal编程技术网

C# 为什么我这里有锁?

C# 为什么我这里有锁?,c#,.net,multithreading,performance,locking,C#,.net,Multithreading,Performance,Locking,请参阅以下表示并行foreach所做工作的并发性能分析: 在循环内部,每个线程从数据库读取数据并进行处理。线程之间没有锁,因为每个线程处理不同的数据 由于未知原因,foreach的所有线程中似乎都存在周期性锁(请参见黑色垂直矩形)。如果您看到选定的锁定段(暗红色段),您将看到堆栈显示StockModel.Quotence构造函数中锁定的线程。那里的代码只构建了两个空列表 我在某个地方读到,这可能是由GC引起的,因此我将垃圾收集更改为在服务器模式下运行,并使用: <runtime>

请参阅以下表示并行foreach所做工作的并发性能分析:

在循环内部,每个线程从数据库读取数据并进行处理。线程之间没有锁,因为每个线程处理不同的数据

由于未知原因,foreach的所有线程中似乎都存在周期性锁(请参见黑色垂直矩形)。如果您看到选定的锁定段(暗红色段),您将看到堆栈显示StockModel.Quotence构造函数中锁定的线程。那里的代码只构建了两个空列表

我在某个地方读到,这可能是由GC引起的,因此我将垃圾收集更改为在服务器模式下运行,并使用:

<runtime>
    <gcServer enabled="true"/>
</runtime>

我得到了一个小的改进(大约快10%-15%),但我仍然有垂直锁无处不在

我还将(NOLOCK)添加到所有DB查询中,因为我只读取没有任何差异的数据

这里发生了什么事,有什么线索吗

进行分析的计算机有8个核

编辑:启用Microsoft Symbol服务器后,所有线程都会在wait\u gor\u gc\u done或WaitUntilGCComplete等调用中被阻止。我认为启用GCServer时,每个线程都有一个GC,因此可以避免“垂直”锁定,但事实似乎并非如此。我错了吗


第二个问题:由于机器没有内存压力(使用了8个Gig中的5个),是否有办法延迟GC执行或暂停GC,直到并行foreach结束(或将其配置为较少触发)?

您可以尝试使用
GCLatencyMode.LowLatency请参见此处的相关问题:


我最近尝试了这个,但运气不好。在我正在显示的窗体上缓存图标大小的位图图像时,仍在调用垃圾收集。对我有效的是使用Ants性能分析器和Reflector来查找导致GC的确切调用。收集并解决它

如果StockModel.Quotence类允许,您可以创建一个池来限制创建的新对象的数量。这是他们有时在游戏中使用的一种技术,以防止垃圾回收器在渲染过程中拖延。 下面是一个基本的池实现:

    class StockQuotationPool
    {

        private List<StockQuotation> poolItems;
        private volatile int itemsInPool;

        public StockQuotationPool(int poolSize)
        {
            this.poolItems = new List<StockQuotation>(poolSize);
            this.itemsInPool = poolSize;

        }

        public StockQuotation Create(string name, decimal value)
        {
            if (this.itemsInPool == 0)
            {
                // Block until new item ready - maybe use semaphore.
                throw new NotImplementedException();
            }

            // Items are in the pool, but no items have been created.
            if (this.poolItems.Count == 0)
            {
                this.itemsInPool--;
                return new StockQuotation(name, value);
            }

            // else, return one in the pool
            this.itemsInPool--;

            var item = this.poolItems[0];
            this.poolItems.Remove(item);

            item.Name = name;
            item.Value = value;

            return item;
        }

        public void Release(StockQuotation quote)
        {
            if (!this.poolItems.Contains(quote)
            {
                this.poolItems.Add(quote);
                this.itemsInPool++;
            }
        }

    } 
然后,不再调用new stockquote()构造函数,而是向池请求一个新实例。该池返回一个现有实例(如果需要,可以预先创建它们),并设置所有属性,使其看起来像一个新实例。您可能需要四处游荡,直到找到一个足够大的池来同时容纳线程

下面是你从线程中调用它的方式

    // Get the pool, maybe from a singleton.
    var pool = new StockQuotationPool(100);


    var quote = pool.Create("test", 1.00m);


    try
    {
        // Work with quote

    }
    finally
    {
        pool.Release(quote);
    }

最后,这个类目前不是线程安全的。如果您需要帮助,请告诉我。

如果您正在分配大量对象,并且锁确实是由GC造成的,您是否尝试在开始TPL工作之前强制执行GC.Collect?GC.Collect with GCCollectionMode.Forced。启用Microsoft符号服务器以获得更好的堆栈跟踪。由于等待时间很长,这看起来像是普通的垃圾收集。@SoMoS您可以订阅GC事件或使用PerfMon.exe查看分代收集。如果hickups对你的应用程序来说是个问题,你可以试着减轻GC负担,并在堆栈上分配你的小对象(将它们转换为结构)。Marc Gravell有一篇关于它的优秀博文:如果你浏览上面关于GC基础知识的链接,它会有GC线程和其他线程的漂亮图表,这取决于服务器和工作站模式。对于64位,您仍然被限制为4Gb虚拟内存。@Kim为什么在x64体系结构上限制为4Gb?它实际上是8TB(理论上),但受硬件和页面文件设置的限制。但是CLR不能创建大于2GB的对象。这里没有什么奇怪的。我在短时间内分配了大量的对象,但是机器有很多RAM,所以如果我能做到的话,我更愿意让GC“停止”一段时间。在循环中有分配吗?他们能搬出去吗?请参阅Mmmm,每个循环从数据库读取自己的数据,对其进行处理并生成一个结果,该结果存储起来以供进一步处理。之后,生成的数据将被丢弃。我没办法避免…好主意,我会考虑的。问题是,由于之前没有考虑到这一点,我不确定何时可以调用该版本所需的逻辑。我已经更新了代码以包含try/finally。您不希望在发生异常时丢失对象。至于何时调用pool.Release(),我想说的基本上是在它超出范围之前,如果它在方法中。如果它存储在对象的某个字段中,则可以使该对象成为一次性对象,并在Dispose方法中调用Release()。你越早发布它,你的池就需要越小越好。是的,我考虑过实现IDisposable,但是同一个对象是几个类的实例方法,并且包含在几个列表中。目前,确定一个实例在别处何时不需要,何时可以被释放并不简单。
    // Get the pool, maybe from a singleton.
    var pool = new StockQuotationPool(100);


    var quote = pool.Create("test", 1.00m);


    try
    {
        // Work with quote

    }
    finally
    {
        pool.Release(quote);
    }