Непрерывный асинхронный c класс пингера: InvalidOperation on Progress (список изменен во время перечисления) - PullRequest
0 голосов
/ 22 апреля 2020

Я пытаюсь написать класс, который непрерывно пингует «в фоновом режиме» список одного или нескольких устройств и сообщает о результатах объекту Progress в главном классе GUI. Я очень новичок в async / await и Tasks, но мой код, кажется, отлично работает на моем p c, даже если я знаю, что он не очень хорошо разработан. Но когда я запускаю приложение на другом компьютере, оно генерирует исключение InvalidOperationException из объекта Progress, так как словарь изменяется. Я думаю, что это происходит, когда я назначаю новый PingService той же переменной. Класс PingService начинает пинговать (SendPingAsyn c) список IP-адресов для функции с инф. l oop, который обновляет объект PingServiceReply для каждой итерации списка и затем «сообщает» его объекту Progress.

Как это можно сделать, и что я делаю неправильно?

Вот код.

Класс PingSeviceReply

public class PingServiceReply
{
    private Dictionary<string, bool> ipDictionary;

    internal PingServiceReply()
    {
        ipDictionary = new Dictionary<string, bool>();
    }

    public void SetIpStatus(string ip, bool status)
    {
        if (ipDictionary.ContainsKey(ip))
        {
            ipDictionary[ip] = status;
        }
        else
        {
            ipDictionary.Add(ip, status);

        }
    }

    public Dictionary<string, bool> GetStatusDictionary()
    {
        return ipDictionary;
    }

    public bool AreAllOnline()
    {
        foreach (bool value in ipDictionary.Values)
        {
            if (!value)
            {
                return false;
            }
        }
        return true;

    }

}

Класс PingService

    public class PingService
{
    private string name;
    private List<string> ipList;
    private IProgress<PingServiceReply> progress;
    private CancellationTokenSource cts;
    private int timeout;
    private Task tsk;

    public PingService(string name, List<string> ipList, int timeoutInMillliseconds, IProgress<PingServiceReply> progress)
    {
        this.name = name;
        this.ipList = ipList;
        this.progress = progress;
        this.timeout = timeoutInMillliseconds;
    }

    public PingService(string name, string ip, int timeoutInMillliseconds, IProgress<PingServiceReply> progress)
    {
        this.name = name;
        this.ipList = new List<string>();
        ipList.Add(ip);
        this.progress = progress;
        this.timeout = timeoutInMillliseconds;
    }

    public PingService()
    {
        cts = new CancellationTokenSource();
    }

    private async Task ContinuousPingAsync()
    {
        var ping = new System.Net.NetworkInformation.Ping();
        PingReply singleResponse;
        PingServiceReply ruleResponse = new PingServiceReply();


        while (!cts.Token.IsCancellationRequested)
        {

            foreach (string ip in ipList)
            {
                singleResponse = await ping.SendPingAsync(ip, timeout).ConfigureAwait(false);
                ruleResponse.SetIpStatus(ip, singleResponse.Status == IPStatus.Success);
            }
            progress.Report(ruleResponse);


        }

    }

    public void StartService()
    {
        cts = new CancellationTokenSource();
        tsk = ContinuousPingAsync();
    }

    public void StopService()
    {
        cts.Cancel();
    }

    public string GetName()
    {
        return name;
    }

}

Объект прогресса

IProgress<PingServiceReply> pingProgress = new Progress<PingServiceReply>(HandleSinglePing);
    private void HandleSinglePing(PingServiceReply report)
    {
        if (report.AreAllOnline())
        {
            //online
        }
        else
        {
            //offline
        }

    }

Как использовать все в классе gui, в поле со списком событие selectedItemChanged

    comboService.StopService();
    comboService = new PingService("Combo", (string) comboBox.SelectedItem, 1000, pingProgress);
    comboService.StartService();
...