我为什么在这里有锁? [英] Why do I have a lock here?

查看:32
本文介绍了我为什么在这里有锁?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请参阅以下并发性能分析,这些分析代表了并行foreach所做的工作:

See the following concurrent performance analysis representing the work done by a parallel foreach:

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

Inside the loop each thread reads data from the DB and process it. There are no locks between threads as each one process different data.

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

Looks like there are periodic locks in all the thread of the foreach due to unknown reasons (see the black vertical rectangles). If you see the selected locked segment (the dark red one) you will see that the stack shows the thread locked at StockModel.Quotation constructor. The code there just constructs two empty lists!

我在某处读到这可能是由GC引起的,所以我将垃圾回收更改为以以下方式在服务器模式下运行:

I've read somewhere that this could be caused by the GC so I've changed the garbage collection to run in server mode with:

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

我做了一个小改进(快了大约10%-15%),但是到处仍然有垂直锁.

I got a small improvement (about 10% - 15% faster) but I still have the vertical locks everywhere.

我还向所有数据库查询中添加了WITH(NOLOCK),因为我只读取数据而没有任何区别.

I've also added to all the DB queries the WITH(NOLOCK) as I'm only reading data without any difference.

这里有什么暗示吗?

完成分析的计算机具有8个内核.

The computer where the analysis has been done has 8 cores.

编辑:启用Microsoft Symbol服务器后,所有线程都在诸如wait_gor_gc_done或WaitUntilGCComplete之类的调用上被阻止.我以为启用GCServer时,每个线程都有一个GC,因此可以避免垂直"锁定,但事实并非如此.我错了吗?

After enabling Microsoft Symbol servers turns out that all threads are blocked on calls like wait_gor_gc_done or WaitUntilGCComplete. I thought that enabling GCServer I had one GC for each thread so I would avoid the "vertical" lock but seems that it's not the case. Am I wrong?

第二个问题:由于计算机没有内存压力(使用了8个演出中的5个),有没有一种方法可以延迟GC执行或暂停它,直到并行foreach结束(或将其配置为减少触发频率)?

Second question: as the machine is not under memory pressure (5 of 8 gigs are used) is there a way to delay the GC execution or to pause it until the parallel foreach ends (or to configure it to fire less often)?

推荐答案

如果StockModel.Quotation类允许这样做,则可以创建一个池来限制创建的新对象的数量.他们有时在游戏中使用这种技术来防止垃圾收集器停滞在渲染中间.

If your StockModel.Quotation class allows for it, you could create a pool to limit the number of new objects created. This is a technique they sometimes use in games to prevent the garbage collector stalling in the middle of renders.

这是一个基本的池实现:

Here's a basic pool implementation:

    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++;
            }
        }

    } 

假设StockQuotation看起来像这样:

That's assuming that the StockQuotation looks something like this:

  class StockQuotation
    {
        internal StockQuotation(string name, decimal value)
        {
            this.Name = name;
            this.Value = value;
        }


        public string Name { get; set; }
        public decimal Value { get; set; }
    }

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

Then instead of calling the new StockQuotation() constructor, you ask the pool for a new instance. The pool returns an existing instance (you can precreate them if you want) and sets all the properties so that it looks like a new instance. You may need to play around until you find a pool size that is large enough to accommodate the threads at the same time.

这是您从线程中调用它的方式.

Here's how you'd call it from the thread.

    // 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);
    }

最后,该类当前不是线程安全的.让我知道您是否需要帮助.

Lastly, this class isn't thread safe at the moment. Let me know if you need any help with making it so.

这篇关于我为什么在这里有锁?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆