Quelle est la meilleure approche pour atteindre l'unicité dans un objet partagé par plusieurs threads?

StackOverflow https://stackoverflow.com/questions/9031553

Question

Je suis intéressé par la synchronisation de mes appels de fonction vers la base de données + d'autres fonctions pour créer des mesures pour la performance de mes applications.J'ai utilisé chronomètre et un objet métrique, mais il ne semble pas toujours donner des valeurs correctes.Parfois, le temps écoulé pour appeler une fonction est exactement le même pour tous les appels irréalistes ...

J'ai découvert que la raison du problème est due aux valeurs de propriétés des objets métriques.Les valeurs d'un objet métrique sont écrasées lorsque d'autres instances des métriques générées par d'autres threads sont attribuées des valeurs.Il semble que les valeurs de propriétés soient par référence, bien qu'une nouvelle instance soit créée par chaque thread.

Quelle est la meilleure approche pour atteindre l'unicité dans un objet partagé par plusieurs threads?

Code ci-dessous:

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

Toute aide sera très appréciée. Merci, R

Était-ce utile?

La solution

What it seems that you want to do is create a metrics object for each iteration, and then aggregate them at the end:

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

Autres conseils

You need to change the scope of your Stopwatch and Metrics variables.

Currently, each thread shares the same Metrics variable. As soon as a thread enters the try block, it creates a new instance of Metrics (correctly), but puts it a shared variable (incorrectly). All other threads will see that new instance when reading the shared variable until the next thread comes along and start the whole process over.

move

private Metrics Metrics;
private Stopwatch Stopwatch;

to just inside your loop

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

This will give each iteration through the loop it's own variable in which to store it's own instance of the the object.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top