【问题标题】:What's the best approach to achieve uniqueness in an object shared by multiple threads?在多个线程共享的对象中实现唯一性的最佳方法是什么?
【发布时间】:2012-01-27 09:58:51
【问题描述】:

我有兴趣为我的函数调用计时数据库 + 其他函数来为我的应用程序的性能构建一些指标。我使用了 Stopwatch 和一个指标对象,但它似乎并没有始终如一地给出正确的值。有时调用一个函数所用的时间对于所有调用都是完全相同的,这是不现实的......

我发现问题的原因在于 Metrics 对象属性值。当其他线程生成的 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);
        }
    }
});

任何帮助将不胜感激。 谢谢, 回复

【问题讨论】:

  • Metrics 类是什么样的?

标签: c# concurrency parallel.foreach


【解决方案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 但将Stopwatch = new Stopwatch() 行更改为var ...,去掉私有行,否则每个线程都共享同一个秒表:)
  • 非常感谢克里斯和安德烈亚斯。我不完全确定它是否有助于在循环中进行声明,因为在尝试之后,在某些情况下我仍然会得到重复。巧合似乎太好了。我在某处读到 Stopwatch 不是与多线程应用程序一起使用的理想类。可能是秒表给出了错误的值!?
【解决方案2】:

您需要更改 Stopwatch 和 Metrics 变量的范围。

目前,每个线程共享相同的 Metrics 变量。一旦线程进入 try 块,它就会(正确地)创建一个新的 Metrics 实例,但将其放入一个共享变量(错误地)。所有其他线程将在读取共享变量时看到该新实例,直到下一个线程出现并重新开始整个过程​​。

移动

private Metrics Metrics;
private Stopwatch Stopwatch;

就在你的循环内

Parallel.ForEach(RequestBag, lOptions, (lItem, loopState) =>
{
    private Metrics Metrics;
    private Stopwatch Stopwatch;
...

这将为循环中的每个迭代提供它自己的变量,用于存储它自己的对象实例。

【讨论】:

    猜你喜欢
    • 2015-03-14
    • 2016-12-21
    • 1970-01-01
    • 1970-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多