我感兴趣的是计时调用数据库+其他函数来为我的应用程序的性能构建一些指标。我使用了秒表和一个指标对象,但似乎并不总是给出正确的值。有时调用函数所用的时间与所有调用完全相同,这是不现实的......
我发现问题的原因是由Metrics对象属性值引起的。当其他线程生成的度量标准的其他实例被赋值时,将覆盖一个Metrics对象的值。虽然每个线程创建一个新实例,但似乎属性值是每个引用。
在多个线程共享的对象中实现唯一性的最佳方法是什么?
以下代码:
private Metrics Metrics;
private Stopwatch Stopwatch;
private int DegreeOfParallelism { get { return Convert.ToInt32(ConfigurationManager.AppSettings["DegreeOfParallelism"].ToString()); } }
var lOptions = new ParallelOptions() { MaxDegreeOfParallelism = DegreeOfParallelism };
Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
if (!string.IsNullOrEmpty(lItem.XmlRequest))
{
try
{
Metrics = new Metrics();
Stopwatch = new Stopwatch();
Stopwatch.Start();
ObjRef = new Object();
lItem.XmlRequest = ObjRef.GetDecision(Username, Password);
Stopwatch.Stop();
Metrics.ElapsedTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
Stopwatch.Restart();
if (!string.IsNullOrEmpty(DBConnectionString))
{
DataAccess = new DataAccess2(DBConnectionString);
DataAccess.WriteToDB(lItem.XmlRequest);
}
Stopwatch.Stop();
Metrics.DbFuncCallTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
}
catch (Exception pEx)
{
KeepLog(pEx);
Metrics.HasFailed = true;
}
finally
{
ProcessedIdsBag.Add(lItem.OrderId);
Metrics.ProcessedOrderId = lItem.OrderId;
Metrics.DegreeOfParallelism = DegreeOfParallelism;
Metrics.TotalNumOfOrders = NumberOfOrders;
Metrics.TotalNumOfOrdersProcessed = ProcessedIdsBag.Count;
pBackgroundWorker.ReportProgress(Metrics.GetProgressPercentage(NumberOfOrders, ProcessedIdsBag.Count), Metrics);
RequestBag.TryTake(out lItem);
}
}
});
非常感谢任何帮助。 谢谢, [R
答案 0 :(得分:1)
您希望做的是为每次迭代创建一个指标对象,然后在最后聚合它们:
private ConcurrentBag<Metrics> allMetrics = new ConcurrentBag<Metrics>();
private int DegreeOfParallelism { get { return Convert.ToInt32(ConfigurationManager.AppSettings["DegreeOfParallelism"].ToString()); } }
var lOptions = new ParallelOptions() { MaxDegreeOfParallelism = DegreeOfParallelism };
Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
if (!string.IsNullOrEmpty(lItem.XmlRequest))
{
try
{
var Metrics = new Metrics();
var Stopwatch = new Stopwatch();
Stopwatch.Start();
ObjRef = new Object();
lItem.XmlRequest = ObjRef.GetDecision(Username, Password);
Stopwatch.Stop();
Metrics.ElapsedTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
Stopwatch.Restart();
if (!string.IsNullOrEmpty(DBConnectionString))
{
DataAccess = new DataAccess2(DBConnectionString);
DataAccess.WriteToDB(lItem.XmlRequest);
}
Stopwatch.Stop();
Metrics.DbFuncCallTime = string.Format("{0:0.00}", Stopwatch.Elapsed.TotalSeconds);
}
catch (Exception pEx)
{
KeepLog(pEx);
Metrics.HasFailed = true;
}
finally
{
ProcessedIdsBag.Add(lItem.OrderId);
Metrics.ProcessedOrderId = lItem.OrderId;
Metrics.DegreeOfParallelism = DegreeOfParallelism;
Metrics.TotalNumOfOrders = NumberOfOrders;
Metrics.TotalNumOfOrdersProcessed = ProcessedIdsBag.Count;
pBackgroundWorker.ReportProgress(Metrics.GetProgressPercentage(NumberOfOrders, ProcessedIdsBag.Count), Metrics);
RequestBag.TryTake(out lItem);
allMetrics.add(Metrics);
}
}
});
// Aggregate everything in AllMetrics here
答案 1 :(得分:1)
您需要更改秒表和指标变量的范围。
目前,每个线程共享相同的Metrics变量。一旦线程进入try块,它就会创建一个新的Metrics实例(正确),但会将其作为共享变量(错误地)。所有其他线程在读取共享变量时将看到新实例,直到下一个线程出现并开始整个过程。
移动
private Metrics Metrics;
private Stopwatch Stopwatch;
到你的循环里面
Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
private Metrics Metrics;
private Stopwatch Stopwatch;
...
这将通过循环给每个迭代它自己的变量,在该变量中存储它自己的对象实例。