Parellel.ForEach, возвращающийся перед методом объекта, который делает вызовы API с ограниченной скоростью - PullRequest
0 голосов
/ 14 февраля 2019

Я работаю над плагином для программы, которая должна делать вызовы API, я ранее делал их все синхронно, что, ну, это работало, было медленно.

Чтобы бороться с этим, я пытаюсь сделатьвызывает асинхронно, я могу сделать 10 в секунду, поэтому я пробовал следующее:

            Parallel.ForEach(
                             items.Values,
                             new ParallelOptions { MaxDegreeOfParallelism = 10 },
                             async item => {
                             await item.UpdateMarketData(client, HQOnly.Checked, retainers);
                             await Task.Delay(1000);
                             }
                         );

клиент является объектом HttpClient, а остальное используется для создания вызова API или для материала, который делается в результатевызов API.Каждый раз, когда item.UpdateMarketData () вызывается 1, и выполняется только 1 вызов API.

Этот код, кажется, завершается очень быстро, и, насколько я понимаю, программа должна дождаться завершения Parallel.ForEach (), прежде чем продолжить.

данные, которые должны быть установлены элементом.UpdateMarketData () также не устанавливается.Чтобы удостовериться, я даже установил MaxDegreeOfParallelism = 1 и задержку на 3 секунды, и он все еще завершился очень быстро, несмотря на то, что осталось ~ 44 элемента.Буду признателен за любую помощь.

UpdateMarketData () включен ниже на случай, если это уместно

public async Task UpdateMarketData(TextBox DebugTextBox,HttpClient client, bool HQOnly, List<string> retainers)
    {
        HttpResponseMessage sellers_result = null;
        try
        {
            sellers_result = await client.GetAsync(String.Format("www.apiImCalling/items/{0}?key=secretapikey", ID));

        }
        catch (Exception e)
        {
            System.Windows.Forms.MessageBox.Show(String.Format("{0} Exception caught.", e));
            sellers_result = null;
        }
        var results = JsonConvert.DeserializeObject<RootObjectMB>(sellers_result.Content.ReadAsStringAsync().Result);
        int count = 0;
        OnMB = false;
        LowestOnMB = false;
        LowestPrice = int.MaxValue;
        try
        {
            foreach (var x in results.Prices)
            {

                if (x.IsHQ | !(HQOnly && RequireHQ))
                {
                    count++;
                    if (count == 1)
                    {
                        LowestPrice = x.PricePerUnit;
                    }
                    if (retainers.Contains(x.RetainerName))
                    {
                        Retainer = x.RetainerName;
                        OnMB = true;
                        Price = x.PricePerUnit;
                        if (count == 1)
                        {
                            LowestOnMB = true;
                        }
                    }
                    if (LowestPrice == x.PricePerUnit && x.RetainerName != Retainer)
                    {
                        LowestOnMB = false;
                    }

                }

            }

        }
        catch (Exception e)
        {
            System.Windows.Forms.MessageBox.Show(String.Format("{0} Exception caught.", e));

        }
    }

Ответы [ 2 ]

0 голосов
/ 14 февраля 2019

async не работает с Parallel.Один - асинхронный, другой - параллельный, и это два совершенно разных стиля параллелизма.

Чтобы ограничить параллелизм асинхронных операций, используйте SemaphoreSlim.Например:

var mutex = new SemaphoreSlim(10);
var tasks = items.Values.Select(item => DoUpdateMarketData(item)).ToList();
await Task.WhenAll(tasks);

async Task DoUpdateMarketData(Item item)
{
  await mutex.WaitAsync();
  try
  {
    await item.UpdateMarketData(client, HQOnly.Checked, retainers);
    await Task.Delay(1000);
  }
  finally { mutex.Release(); }
}

Вы можете найти мою книгу полезной;это описано в рецепте 11.5.

0 голосов
/ 14 февраля 2019

Вместо параллельного цикла вы можете использовать Задачу и ждать, пока все задачи завершатся.

var tasks = new List<Task>();
foreach (var val in items.Values)
    tasks.Add(Task.Factory.StartNew(val.UpdateMarketData(client, HQOnly.Checked, retainers)));

 try
 {
    // Wait for all the tasks to finish.
    Task.WaitAll(tasks.ToArray());
    //make use of WhenAll method if you dont want to block thread, and want to use async/await 

    Console.WriteLine("update completed");
  }
  catch (AggregateException e)
  {
     Console.WriteLine("\nThe following exceptions have been thrown by WaitAll(): (THIS WAS EXPECTED)");
    for (int j = 0; j < e.InnerExceptions.Count; j++)
    {
        Console.WriteLine("\n-------------------------------------------------\n{0}", e.InnerExceptions[j].ToString());
    }
  }
...