请参阅以下并发性能分析,该分析表示并行foreach所做的工作:
在循环内部,每个线程从数据库中读取数据并对其进行处理。线程之间没有锁,因为每个处理不同的数据。
由于未知原因,看起来foreach的所有线程都有定期锁定(请参阅黑色垂直矩形)。如果您看到所选的锁定段(深红色段),您将看到堆栈显示在StockModel.Quotation构造函数处锁定的线程。那里的代码只构造了两个空列表!
我在某处读过这可能是由GC造成的,因此我将垃圾收集更改为在服务器模式下运行:
<runtime>
<gcServer enabled="true"/>
</runtime>
我得到了一个小的改进(大约10% - 快15%),但我仍然到处都有垂直锁。
我还在所有数据库查询中添加了WITH(NOLOCK),因为我只读数据而没有任何区别。
这里有什么暗示?
进行分析的计算机有8个核心。
编辑:启用Microsoft Symbol服务器后,所有线程都会在wait_gor_gc_done或WaitUntilGCComplete等调用中被阻止。我认为启用GCServer我为每个线程都有一个GC,所以我会避免“垂直”锁定,但似乎并非如此。我错了吗?
第二个问题:由于机器没有内存压力(使用了8个演出中的5个)有一种方法可以延迟GC执行或暂停它直到并行foreach结束(或将其配置为较少发射) ?
答案 0 :(得分:3)
如果您的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);
}
最后,此类目前不是线程安全的。如果您需要任何帮助,请告诉我。
答案 1 :(得分:0)
您可以尝试使用GCLatencyMode.LowLatency;
在此处查看相关问题:Prevent .NET Garbage collection for short period of time
我最近尝试了这个没有运气。在我正在显示的表单上缓存Icon大小的位图图像时,仍然会调用垃圾收集。对我有用的是使用Ants性能分析器和Reflector来查找导致GC.Collect并解决它的确切调用。