Я пытаюсь вызвать метод в цикле. Он должен быть вызван только 20 раз за 10 секунд.Я использую семафор, как показано ниже - PullRequest
0 голосов
/ 08 февраля 2019

При использовании приведенного ниже кода, во-первых, некоторые звонки не выполняются, скажем, из 250 сделано 238 звонков, а остальные нет. Во-вторых, я не уверен, что звонки совершаются из расчета 20 звонков в10 секунд.

public List<ShowData> GetAllShowAndTheirCast()
    {
        ShowResponse allShows = GetAllShows();

        ShowCasts showCast = new ShowCasts();
        showCast.showCastList = new List<ShowData>();


        using (Semaphore pool = new Semaphore(20, 20))
        {
            for (int i = 0; i < allShows.Shows.Length; i++)
            {
                pool.WaitOne();
                Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
                {
                 showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
                }));
                pool.Release();
                t.Start(i);
            }
        }
        //for (int i = 0; i < allShows.Shows.Length; i++)
        //{
        //    showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
        //}

        return showCast.showCastList;
    }

 public ShowData MapResponse(Show s)
    {
        CastResponse castres = new CastResponse();
        castres.CastlistResponse = (GetShowCast(s.id)).CastlistResponse;
        ShowData sd = new ShowData();
        sd.id = s.id;
        sd.name = s.name;
        if (castres.CastlistResponse != null && castres.CastlistResponse.Any())
        {

            sd.cast = new List<CastData>();
            foreach (var item in castres.CastlistResponse)
            {
                CastData cd = new CastData();
                cd.birthday = item.person.birthday;
                cd.id = item.person.id;
                cd.name = item.person.name;
                sd.cast.Add(cd);
            }

        }
        return sd;
    }
public ShowResponse GetAllShows()
    {
        ShowResponse response = new ShowResponse();
        string showUrl = ClientAPIUtils.apiUrl + "shows";
        response.Shows = JsonConvert.DeserializeObject<Show[]>(ClientAPIUtils.GetDataFromUrl(showUrl));
        return response;
    }

    public CastResponse GetShowCast(int showid)
    {
        CastResponse res = new CastResponse();
        string castUrl = ClientAPIUtils.apiUrl + "shows/" + showid + "/cast";
        res.CastlistResponse = JsonConvert.DeserializeObject<List<Cast>>(ClientAPIUtils.GetDataFromUrl(castUrl));
        return res;
    }

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

1 Ответ

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

Я предполагаю, что ваша цель - обработать все данные о шоу, но не более 20 одновременно.Для такого рода задач вам, вероятно, следует использовать ThreadPool и ограничить максимальное количество одновременных потоков с помощью SetMaxThreads.

https://docs.microsoft.com/en-us/dotnet/api/system.threading.threadpool?view=netframework-4.7.2

. Необходимо убедиться, что используемая коллекцияхранить ваши результаты является потокобезопасным.

showCast.showCastList = new List<ShowData>();

Я не думаю, что стандартный List является поточно-ориентированным.Потокобезопасная коллекция - ConcurrentBag (есть и другие).Вы можете сделать стандартный список потокобезопасным, но для этого требуется больше кода.После того, как вы закончите обработку и вам понадобятся результаты в списке или массиве, вы можете преобразовать коллекцию в нужный тип.

https://docs.microsoft.com/en-us/dotnet/api/system.collections.concurrent.concurrentbag-1?view=netframework-4.7.2

Теперь к использованию семафора.Что делает ваш семафор, так это обеспечивает одновременное создание максимум 20 потоков.Предполагая, что этот цикл выполняется в главном потоке вашего приложения, ваш семафор не имеет смысла.Чтобы заставить его работать, вам нужно освободить семафор после завершения потока;но вы вызываете поток Start() после вызова Release().Это приводит к выполнению потока вне «критической области».

using (Semaphore pool = new Semaphore(20, 20)) {
    for (int i = 0; i < allShows.Shows.Length; i++) {
        pool.WaitOne();
        Thread t = new Thread(new ParameterizedThreadStart((taskId) =>
        {
            showCast.showCastList.Add(MapResponse(allShows.Shows[i]));
            pool.Release();
        }));            
        t.Start(i);
    }
}

Я не тестировал это решение;могут возникнуть дополнительные проблемы.

Другая проблема, связанная с этой программой, заключается в том, что она не ожидает завершения всех потоков.Как только все потоки запущены;Программа закончится.Возможно (и в вашем случае я уверен), что не все потоки завершили свою работу;Вот почему ~ 240 пакетов данных сделано, когда программа заканчивается.

thread.Join();

Но при вызове сразу после Start() он остановит основной поток до его завершения, поэтому для поддержания параллельности программы необходимо создать список потоков и Join() их в конце программы.Это не лучшее решение.Как ждать во всех потоках, которые программа может добавить к ThreadPool

Подождите, пока все потоки не закончат свою работу в ThreadPool

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

Возможное решение - создать метод, создающий поток.Наличие его в отдельном методе оценивает allShows.Shows[i] до show перед следующим проходом цикла.

public void CreateAndStartThread(Show show, Semaphore pool, ShowCasts showCast)
{
        pool.WaitOne();
        Thread t = new Thread(new ParameterizedThreadStart((s) => {
            showCast.showCastList.Add(MapResponse((Show)s));
            pool.Release();
        }));
    t.Start(show);
}

Параллельное программирование сложно, и я настоятельно рекомендую выполнить некоторые упражнения с примерами распространенных ошибок.В книгах по программированию на C # наверняка есть глава или две на эту тему.На эту тему можно найти множество онлайн-курсов и учебных пособий.

Редактировать: Рабочее решение.Тем не менее могут возникнуть некоторые проблемы.

    public ShowCasts GetAllShowAndTheirCast()
        {
            ShowResponse allShows = GetAllShows();

            ConcurrentBag<ShowData> result = new ConcurrentBag<ShowData>();
            using (var countdownEvent = new CountdownEvent(allShows.Shows.Length))
            {
                using (Semaphore pool = new Semaphore(20, 20))
                {
                    for (int i = 0; i < allShows.Shows.Length; i++)
                    {
                        CreateAndStartThread(allShows.Shows[i], pool, result, countdownEvent);
                    }

                    countdownEvent.Wait();
                }     
            }
            return new ShowCasts() { showCastList = result.ToList() };
        }

    public void CreateAndStartThread(Show show, Semaphore pool, ConcurrentBag<ShowData> result, CountdownEvent countdownEvent)
        {
            pool.WaitOne();
            Thread t = new Thread(new ParameterizedThreadStart((s) =>
            {
                result.Add(MapResponse((Show)s));
                pool.Release();
                countdownEvent.Signal();
            }));

            t.Start(show);
        }
...