Nano Hash - криптовалюты, майнинг, программирование

Регулярно запускать асинхронный метод с указанным интервалом

Мне нужно опубликовать некоторые данные в службе из веб-приложения C #. Сами данные собираются, когда пользователь использует приложение (своего рода статистика использования). Я не хочу отправлять данные в службу во время каждого запроса пользователя, я бы предпочел собирать данные в приложении и отправлять затем все данные в одном запросе в отдельном потоке, который не обслуживает запросы пользователей (я имею в виду пользователю не нужно ждать обработки запроса сервисом). Для этого мне нужен своего рода setInterval аналог JS - запуск функции каждые X секунд для сброса всех собранных данных в сервис.

Я обнаружил, что _2 _ предоставляет нечто похожее ( Elapsed). Однако это позволяет запустить метод только один раз, но это не большая проблема. Основная сложность в том, что требуется подпись

void MethodName(object e, ElapsedEventArgs args)

а я хотел бы запустить асинхронный метод, который будет вызывать веб-службу (входные параметры не важны):

async Task MethodName(object e, ElapsedEventArgs args)

Может кто-нибудь посоветует, как решить описанную задачу? Любые советы приветствуются.


  • Если Timer не хочет сотрудничать с вами в AutoReset - Получает или задает значение, указывающее, должен ли Timer вызывать событие Elapsed каждый раз по истечении указанного интервала или только после его первого истечения. потому что вы отказались читать ссылку, которую вы предоставили, чем вам не повезло ... :) Примечание - как правило, лучше использовать внешние уведомления, если вам нужны надежные интервалы / избегайте повторного цикла процесса - bing.com/search?q=asp.net+repeating+task должен дать вам info (или фоновые задачи ASP.Net). 26.05.2015
  • @AlexeiLevenkov хорошая насмешка. Спасибо за советы, в следующий раз исследуем более внимательно. Однако это не решает главного вопроса. 26.05.2015
  • Поскольку он не решает основную проблему, это комментарий ... Ответ i3arnon дает вам ссылки, как вызвать async в режиме «огонь и забыть» (также известный как метод вызова async из обработчика событий) ... 26.05.2015

Ответы:


1

Эквивалент async - это while цикл с Task.Delay (который внутри использует System.Threading.Timer):

public async Task PeriodicFooAsync(TimeSpan interval, CancellationToken cancellationToken)
{
    while (true)
    {
        await FooAsync();
        await Task.Delay(interval, cancellationToken)
    }
}

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

Хотя это актуально для .Net в целом, в ASP.Net опасно поджечь и забыть. Для этого есть несколько решений (например, HangFire), некоторые из них задокументированы в Запустить и забыть в ASP.NET, Стивен Клири другие в Как запустить Фоновые задачи в ASP.NET, Скотт Хансельман

26.05.2015
  • Должен ли я писать await PeriodicFooAsync или просто var result = PeriodicFooAsync? Нужно ли нам в этом случае ждать? 11.10.2016
  • @Arv, и это зависит от того, хотите ли вы дождаться завершения операции или нет. result в вашем случае будет содержать только задачу, следующая строка после этого произойдет немедленно. await будет блокироваться асинхронно до тех пор, пока периодическая задача не будет отменена. 11.10.2016
  • @Arv и вы, вероятно, захотите сохранить эту задачу где-нибудь, не дожидаясь ее (при условии, что она будет жить вечно, пока ваше приложение не закроется), а затем вы можете дождаться ее перед закрытием. 11.10.2016
  • Поскольку у меня есть несколько PeriodicAsycMethods, я обязательно должен сохранить задачу и избегать использования await, Спасибо. 11.10.2016
  • Мне потребовалось время, чтобы понять, как написать его в общем виде, чтобы принимать любую задачу и запускать ее периодически, мне действительно помог ваш другой ответ stackoverflow.com/questions/27918061/. Возможно, вы сможете обновить свой ответ, чтобы другие не столкнулись с той же проблемой. @ i3arnon 18.12.2016
  • Это не периодично, так как await FooAsync будет иметь ненулевое время выполнения. Например, если FooAsync занимает 500 мс и запускается каждый час, то вы отключитесь на 10 минут менее чем за 2 месяца. 06.11.2017
  • @mayu в том-то и дело. Представьте, что вы запускаете его каждые 5 минут, а это занимает 7. Он не нуждается в согласовании с часами (он не начинает согласовываться с самого начала). Это должно происходить каждые x интервалов, не переезжая через себя. 06.11.2017
  • @ i3arnon Конечно, это решает проблему OP. Я просто указываю, что это не очень регулярно. Он не запускается каждые n часов / минут / секунд. 07.11.2017
  • @mayu я понимаю. Я говорю, что этого не должно быть. Если вы работаете с ограниченным интервалом (что вы, вероятно, в любом случае не можете гарантировать точно), вы рискуете, что ваше действие будет выполняться за интервал и вызвать непреднамеренный каскадный параллелизм. Это может привести к тому, что операции будут выполняться еще медленнее, что приведет к большему параллелизму и т. Д. Это плохая практика. 07.11.2017
  • Несмотря на то, что использование async и await является модным и крутым подходом, вы должны помнить, что async не запускает вашу задачу в отдельном потоке, он просто откладывает выполнение, как это делает javascript. Пожалуйста, подумайте об использовании потоков, если вы будете работать в ситуациях, когда важна производительность, и вы сталкиваетесь с ситуацией ограничения множества ресурсов, потребляемых системой. 30.07.2019
  • Предложение по улучшению: вы могли бы иметь более последовательный интервал, создав задачу Task.Delay перед ожиданием FooAsync и ожидая его после. Это будет иметь значение, если FooAsync синхронно выполнит некоторую нетривиальную работу перед возвратом Task. 30.06.2020
  • @TheodorZoulias Это сделано намеренно. Представьте, что произойдет, если FooAsync по какой-то причине займет больше интервала задержки ... 01.07.2020
  • Если ожидание FooAsync занимает больше времени, чем interval, то с моим предложением период будет продлен и, по существу, будет определяться продолжительностью FooAsync. В этом случае более точное имя для аргумента interval будет minInterval. Между прочим, текущая версия PeriodicFooAsync не периодическая. Он не пытается поддерживать постоянный период между каждым вызовом FooAsync. Вместо этого он просто накладывает задержку между завершением одной операции и началом следующей. 02.07.2020
  • @TheodorZoulias снова, это намеренно. Ваше предложение (в этом случае) вызовет постоянный жесткий (while (true)) цикл, что почти всегда является плохой идеей. Если вам нужна точная частота вращения педалей, решение должно быть более надежным. 02.07.2020

  • 2

    Самый простой способ сделать это - использовать Задачи и простой цикл:

    public async Task StartTimer(CancellationToken cancellationToken)
    {
    
       await Task.Run(async () =>
       {
          while (true)
          {
              DoSomething();
              await Task.Delay(10000, cancellationToken);
              if (cancellationToken.IsCancellationRequested)
                  break;
          }
       });
    
    }
    

    Если вы хотите остановить поток, просто прервите токен:

    cancellationToken.Cancel();
    
    26.05.2015
  • 1. Нет необходимости в Task.Run. 2. DoSomething должен быть асинхронным. 3. Вы должны дождаться получения формы задания Task.Delay 26.05.2015
  • Этот код бросает System.Threading.Tasks.TaskCanceledException в await Task.Delay(...) строку. 28.06.2017
  • Это не периодично, поскольку время выполнения DoSomething будет отличным от нуля. Например, если DoSomething занимает 500 мс и запускается каждый час, вы отключитесь на 10 минут менее чем за 2 месяца. 06.11.2017
  • @tymtam, если DoSomehthing является асинхронным и мы НЕ ждем его, тогда он ничего не добавит к нашему периодическому выполнению. Единственная проблема будет в том, что DoSomethings займет больше времени, чем наша периодическая проверка, которую следует решить с помощью блокировки объекта на DoSomething, чтобы избежать повторного входа. 19.12.2020
  • Что касается задачи TaskCanceledException, о которой упоминает @pitersmx, это поведение библиотеки задач по умолчанию и то, что каждая задача должна реализовывать в соответствии с этим исходный код. Вы можете заключить Task.Delay в блок try / catch или просто игнорировать токен отмены для этой задачи. 19.12.2020

  • 3

    Вот метод, который периодически вызывает асинхронный метод:

    public static async Task PeriodicAsync(Func<Task> taskFactory, TimeSpan interval,
        CancellationToken cancellationToken = default)
    {
        while (true)
        {
            var delayTask = Task.Delay(interval, cancellationToken);
            await taskFactory();
            await delayTask;
        }
    }
    

    Предоставленный taskFactory вызывается каждые interval, а затем ожидается созданный Task. Продолжительность ожидания не влияет на интервал, если только он не превышает его. В этом случае принцип исполнения без перекрытия имеет приоритет, и поэтому период будет увеличен, чтобы соответствовать продолжительности ожидания.

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

    Пример использования:

    Task statisticsUploader = PeriodicAsync(async () =>
    {
        try
        {
            await UploadStatisticsAsync();
        }
        catch (Exception ex)
        {
            // Log the exception
        }
    }, TimeSpan.FromMinutes(5));
    
    04.07.2020
  • Хм. Будет ли это более или менее эффективным, чем использование таймера. Вы проводите разведку? 14.07.2020
  • @sommmen Я считаю, что производительность должна быть одинаковой, потому что и System.Timers.Timer, и Task.Delay используют _ 3_ для внутреннего пользования. 14.07.2020
  • Новые материалы

    Кластеризация: более глубокий взгляд
    Кластеризация — это метод обучения без учителя, в котором мы пытаемся найти группы в наборе данных на основе некоторых известных или неизвестных свойств, которые могут существовать. Независимо от..

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

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

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

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

    Понимание расстояния Вассерштейна: мощная метрика в машинном обучении
    В обширной области машинного обучения часто возникает необходимость сравнивать и измерять различия между распределениями вероятностей. Традиционные метрики расстояния, такие как евклидово..

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..