How to do a parallelism or asynchronous call in a lambda

Asked

Viewed 65 times

0

I have this query

qry.ForEach(q =>
            {
                var product = new Product();

                product.CategoryId = q.CategoryId;
                product.AuthorizeMaximumPercentageAlteration = q.AuthorizeMaximumPercentageAlteration;
                product.AuthorizeMinimumPercentageAlteration = q.AuthorizeMinimumPercentageAlteration;
                product.HaveMaximumPercentage = q.HaveMaximumPercentage;
                product.HaveMinimumPercentage = q.HaveMinimumPercentage;
                product.Hidden = q.Hidden;
                product.ChildrenCategoryId = q.ChildrenCategoryId;
                product.Description = q.Description;
                product.DistributionCenterErpId = q.DistributionCenterErpId;
                product.MaximumPercentage = q.MaximumPercentage;
                product.MinimumPercentage = q.MinimumPercentage;
                product.Name = q.Name;
                product.NameHtml = q.NameHtml;
                product.Order = q.Order;
                product.PartNumber = q.PartNumber;
                product.PictureId = q.PictureId;
                product.ShortDescription = q.ShortDescription;
                product.StandardMarkup = q.StandardMarkup;

                //if (product.PictureId.HasValue)
                //    q.PictureFilename = product.Picture.FileName;

                //var pct = product.PictureId.Value > 0 ? q.PictureFilename = product.Picture.FileName : "";

                var parentProducts = _productService.GetParentsOf(product.Id).Select(x => x.PartNumber);
                q.Parents.AddRange(parentProducts);

                var price = _erpPriceService.GetPrice(product, 1, resellerId).Result;

                if (price.BasePriceUSD > 0)
                    q.DolarRate = price.BasePriceBRL / price.BasePriceUSD;

                q.ResellerPriceUSD = price.ResellerPriceUSD;
                q.ResellerPriceBRL = price.ResellerPriceBRL;
                q.BasePriceBRL = price.BasePriceBRL;
                q.BasePriceUSD = price.BasePriceUSD;

            });

In this line the performance is bad

var price = _erpPriceService.GetPrice(product, 1, resellerId).Result;

This is because, as you can see, for each product item, this call is made. This call is a service, which goes to the bank takes the values of each item and returns. This is a long way. If a stride of this, take 10s, for example, in 202 items I would have 2020 seconds, which kills the execution of the entire system. You who are good at this, there is a way, parallel, asynchronous or otherwise, to minimize this impact that is very great. Any help is welcome.

What I want, to be direct, is how I outline this problem, in the line quoted, to improve performance

  • Need the whole object to get the price? Or just a key? I ask because I would probably be able to get the price for all products before getting into that ForEach.

2 answers

1

There is always the possibility to use the Parallel.ForEach (How to: Write a simple Parallel.Foreach loop):

using System.Threading.Tasks;

// ...

Parallel.ForEach(qry, (q) =>
{
    var product = new Product()
    {
        CategoryId = q.CategoryId,
        AuthorizeMaximumPercentageAlteration = q.AuthorizeMaximumPercentageAlteration,
        AuthorizeMinimumPercentageAlteration = q.AuthorizeMinimumPercentageAlteration,
        HaveMaximumPercentage = q.HaveMaximumPercentage,
        HaveMinimumPercentage = q.HaveMinimumPercentage,
        Hidden = q.Hidden,
        ChildrenCategoryId = q.ChildrenCategoryId,
        Description = q.Description,
        DistributionCenterErpId = q.DistributionCenterErpId,
        MaximumPercentage = q.MaximumPercentage,
        MinimumPercentage = q.MinimumPercentage,
        Name = q.Name,
        NameHtml = q.NameHtml,
        Order = q.Order,
        PartNumber = q.PartNumber,
        PictureId = q.PictureId,
        ShortDescription = q.ShortDescription,
        StandardMarkup = q.StandardMarkup
    };

    var parentProducts = _productService.GetParentsOf(product.Id).Select(x => x.PartNumber);

    q.Parents.AddRange(parentProducts);

    var price = _erpPriceService.GetPrice(product, 1, resellerId).Result;

    if (price.BasePriceUSD > 0)
        q.DolarRate = price.BasePriceBRL / price.BasePriceUSD;

    q.ResellerPriceUSD = price.ResellerPriceUSD;
    q.ResellerPriceBRL = price.ResellerPriceBRL;
    q.BasePriceBRL = price.BasePriceBRL;
    q.BasePriceUSD = price.BasePriceUSD;
});

Basically a Thread is created for each record and everything is done in parallel, but I do not know if it will be the most "correct solution".


Another solution would be to create a method GetPrice who received a list of IDs of products (since the 2nd and 3rd parameters are static within the cycle), where then for each instance of the object qry update what is necessary.


That, perhaps, would be the least "heavy" option, not create innumerable threads and may be more performant than the first option.

  • João, about winning performance with Paralell I could not. The problem is exactly in this instruction: var price = _erpPriceService.GetPrice(product, 1, resellerId).Result;. This call to this service, is being held several times and I still could not solve it. Any help is very welcome

  • Then I would go to my second solution proposal!

  • So the problem is it’s a call to a service that’s inside Protheus and I don’t have access. I can create methods, but within that service I have only one method that brings the price, RetornaPreco where I pass one product object, quantity, reseller/user id. Anyway for me to bring everything at once, within a lambda, for example, I would have to create a loop to go through each item of the product and the quantity, although static(1), right, or would have another way smarter than that?

1

Make use of the method Task.Whenall, an alternative, probably more recommended to deal with parallelism.

One way to reformulate this code to run in parallel is like this (I removed the excess code to focus on the answer):

var tasks = new List<Task>();

qry.ForEach(q =>
            {
                var product = new Product();

                // atribuições

                var parentProductsTask = Task.Run(() => 
                {
                    var parentProducts = _productService.GetParentsOf(product.Id).Select(x => x.PartNumber);
                    q.Parents.AddRange(parentProducts);
                });

                var priceTask = Task.Run(() => 
                {
                    var price = _erpPriceService.GetPrice(product, 1, resellerId).Result;

                    if (price.BasePriceUSD > 0)
                        q.DolarRate = price.BasePriceBRL / price.BasePriceUSD;

                    q.ResellerPriceUSD = price.ResellerPriceUSD;
                    q.ResellerPriceBRL = price.ResellerPriceBRL;
                    q.BasePriceBRL = price.BasePriceBRL;
                    q.BasePriceUSD = price.BasePriceUSD;
                });

                tasks.Add(parentProductsTask);
                tasks.Add(priceTask);
            });

await Task.WhenAll(tasks);
  • Daskpark, after the block of qry I put this var model = new CatalogResponse();&#xA; model.CatalogDate = DateTime.UtcNow;&#xA; model.Items = new List<CatalogItemResponse>();&#xA; model.Items.AddRange(qry);, how would I look with the task force now.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.