получение параллельных запросов с таймером - PullRequest
0 голосов
/ 08 января 2020

У меня есть этот сценарий. у меня есть таймер (с autoreset = true), который выполняется каждые, скажем, 2 секунды, и когда таймер срабатывает, он будет go и вызовет 10 различных URL-адресов для сбора данных. каждый дубль в среднем 1se c.

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

Следующее событие не сработает, пока все параллельные запросы не будут завершены, прежде чем таймер сможет повторить попытку, поэтому теоретически он не опрашивает URL-адреса каждый раз. 2 секунды.

public List<string> Urls {get;set;}

private Timer SetupTimer()
    {
        var timer = new Timer(2000);
        timer.AutoReset = false;
        timer.Elapsed += timer_Elapsed;
        timer.Start();
        return timer;
    }

 private async void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            Parallel.ForEach(Urls, url=>
                {
                    var data= _dataService.GetData(url);
                    SetOnDataRecieved(new DataRecievedEventArgs(data));
                });
        }
        finally
        {
            if (timer == null) return;
            if (!timer.Enabled) { timer.Enabled = true; }
            timer.Start();
        }
    }

Какой лучший способ реализовать это? Спасибо.

1 Ответ

0 голосов
/ 08 января 2020

В вашей реализации нет ничего плохого, если только все URL-адреса, ожидающие самого медленного, не являются проблемой. Вы можете реализовать класс, который инкапсулирует все логи программы c и подключается только к одному URL за раз. Таким образом, ни один URL не будет содержать другой. Каждый URL будет получен как можно быстрее, а затем возобновить двухсекундное ожидание. Что-то вроде этого, вероятно, будет работать (не проверено):

public class UrlWatcher
{
    public string Url { get; set; }
    Timer timer { get; set; } = new Timer();
    DataService _dataService { get; set; }
    //Todo: Implement CancellationToken to gracefully stop the timer.

    //Event to report retrieved data
    public EventHandler<DataReceivedEventArgs> OnDataReceived; 

    public UrlWatcher(string Url, int TimerInterval = 2000)
    {
        //Use your implementation of DataService() here
        _dataService = new DataService(); 
        this.Url = Url;
        timer.AutoReset = false;
        timer.Interval = TimerInterval;
    }

    public void Start() // Call this to start polling the URL
    {
        timer.Start();
    }

    public async void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        try
        {
            var data = await _dataService.GetData(Url);
            OnDataReceived(this, new DataReceivedEventArgs(data));
        }
        finally
        {
            if (timer != null && !timer.Enabled) 
            { 
                timer.Enabled = true;
                timer.Start();
            }
        }
    }
}

Тогда вы просто объявите UrlWatcher для каждого URL, который хотите опросить:

    //Thread-safe collection to hold results
    ConcurrentBag<string> results { get; set; } = new ConcurrentBag<string>();
    List<string> urls { get; set; } = new List<string>() { "www.google.com", "www.facebook.com", "www.yahoo.com" };
    //List to hold URL watchers
    List<UrlWatcher> watchers { get; set; } = new List<UrlWatcher>(); 

    private void StartBtn_Click(object sender, EventArgs e)
    {
        //Create a separate watcher for each URL. Subscribe to the OnDataReceived event. Start the watchers.
        foreach(var url in urls)
        {
            UrlWatcher w = new UrlWatcher(url);
            w.OnDataReceived += (o, oe) => results.Add(oe.CollectedData);
            w.Start();
            watchers.Add(w);
        }
    }
...