Каков наилучший подход для достижения уникальности объекта, совместно используемого несколькими потоками? - PullRequest
2 голосов
/ 27 января 2012

Я заинтересован в том, чтобы синхронизировать мои вызовы функций с базой данных + другие функции, чтобы построить некоторые показатели для производительности моего приложения. Я использовал секундомер и объект метрики, но он не дает правильных значений. Иногда истекшее время вызова функции одинаково для всех вызовов, что нереально ...

Я обнаружил, что причина проблемы заключается в значениях свойств объекта 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);
        }
    }
});

Любая помощь будет очень ценится. Спасибо, R * +1010 *

Ответы [ 2 ]

1 голос
/ 30 января 2012

Вам необходимо изменить область действия переменных Секундомер и Метрики.

В настоящее время каждый поток использует одну и ту же переменную Metrics.Как только поток входит в блок try, он создает новый экземпляр Metrics (правильно), но помещает его в общую переменную (неправильно).Все остальные потоки увидят этот новый экземпляр при чтении общей переменной, пока не появится следующий поток, и не начнут весь процесс заново.

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

Это даст каждой итерации в цикле свою собственную переменную, в которой будет храниться собственный экземпляр объекта.

1 голос
/ 27 января 2012

Похоже, что вы хотите сделать, это создать объект метрики для каждой итерации, а затем объединить их в конце:

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
...