Подтвердить что ты не робот

Почему у меня есть замок?

См. следующий параллельный анализ производительности, представляющий работу, выполняемую параллельным foreach:

enter image description here

Внутри цикла каждый поток считывает данные из БД и обрабатывает его. Между потоками нет блокировок, так как каждый обрабатывает разные данные.

Похоже, что во всех потоках foreach есть периодические блокировки по неизвестным причинам (см. черные вертикальные прямоугольники). Если вы видите выбранный заблокированный сегмент (темно-красный), вы увидите, что стек показывает поток, заблокированный в конструкторе StockModel.Quotation. В коде есть только два пустых списка!

Я где-то читал, что это может быть вызвано GC, поэтому я изменил сборку мусора для запуска в режиме сервера с помощью:

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

У меня небольшое улучшение (примерно на 10% - на 15% быстрее), но у меня все еще есть вертикальные замки повсюду.

Я также добавил ко всем БД запросы WITH (NOLOCK), поскольку я только читаю данные без каких-либо различий.

Любой намек на то, что происходит здесь?

Компьютер, на котором выполнен анализ, имеет 8 ядер.

РЕДАКТИРОВАТЬ: После включения серверов Microsoft Symbol получается, что все потоки заблокированы для вызовов, таких как wait_gor_gc_done или WaitUntilGCComplete. Я думал, что для включения GCServer у меня был один GC для каждого потока, поэтому я бы избегал "вертикальной" блокировки, но, похоже, это не так. Я не прав?

Второй вопрос: поскольку машина не находится под давлением памяти (используется 5 из 8 концертов), есть способ отсрочить выполнение GC или приостановить его до тех пор, пока параллельный выходной сигнал не будет (или настроить его на то, чтобы срабатывать реже)

4b9b3361

Ответ 1

Если ваш класс StockModel.Quotation позволяет это, вы можете создать пул, чтобы ограничить количество созданных новых объектов. Это техника, которую они иногда используют в играх, чтобы предотвратить сборщик мусора в середине рендеринга.

Вот базовая реализация пула:

    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 выглядит примерно так:

  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() вы задаете пул для нового экземпляра. Пул возвращает существующий экземпляр (вы можете предварительно создать их, если хотите) и задает все свойства, чтобы он выглядел как новый экземпляр. Возможно, вам придется поиграть, пока не найдете размер пула, который достаточно большой, чтобы одновременно разместить потоки.

Вот как вы могли бы назвать это из потока.

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

Наконец, этот класс на данный момент не является потокобезопасным. Дайте мне знать, если вам нужна помощь в создании этого.

Ответ 2

Вы можете попробовать использовать GCLatencyMode.LowLatency; См. соответствующий вопрос здесь: Предотвратить сборку мусора .NET за короткий период времени

Недавно я попробовал это без везения. Сбор мусора по-прежнему вызывался при кешировании растровых изображений размеров значков в форме, которую я показывал. То, что работало для меня, - это использовать профилировщик характеристик Ants и Reflector, чтобы найти точные вызовы, которые вызывают GC.Collect и обходить его.