Почему я получаю 'System.ArgumentOutOfRangeException'? - PullRequest
0 голосов
/ 29 ноября 2018

Я работаю над приложением сервер-клиент.Этот вопрос относится к исключению на стороне клиента WindowsFroms, которое я не знаю, почему происходит.
Контекст: Сервер хранит сигналы тревоги для полос движения.Каждая дорожка имеет идентификатор дорожки, и каждая дорожка может «генерировать» больше сигналов тревоги.У каждого будильника есть уникальный идентификатор (guid).Обычно клиенты запрашивают только направляющие сигналов тревоги с сервера, и полностью получаются только те сигналы тревоги, которых нет у клиента.Я хочу эффективно отслеживать аварийные сигналы сервера на стороне клиента в элементе управления DataGridView.Но иногда я получаю 'System.ArgumentOutOfRangeException' при попытке удалить строку из DataGridView, и я не знаю, почему это происходит, поскольку я обнаружил этот элемент его строк непосредственно перед этим.
У меня общий доступ только для клиентабоковые коды.Первым из них является метод-обработчик события таймера.Короче говоря, это собирает идентификаторы laneID от пользователя, а затем запрашивает все направляющие с сервера, которые принадлежат одной из указанных полос.Сравните наши гиды с новыми и попросите новые тревоги.Наконец, добавьте каждый элемент в контрольные строки.

Task task;
private ConcurrentDictionary<string, string> alarmGuidLaneIdDict;

//Windows.Forms.Timer's Tick method with 300 msec interval
private void AlarmRequestTimer_Tick(object sender, EventArgs e)
    {
        //collecting the id-s
        List<string> laneids = new List<string>();
        foreach (LaneView item in layoutPanel.Controls)
        {
            if (item.laneInfo != null)
            {
                laneids.Add(item.laneInfo.Id);
            }
        }
        if (task == null || task.IsCompleted)
        {
            task = new Task(() =>
            {
            //should not do anything if no id present
                if (laneids.Count < 1)
                    return;

                    //getting all existing guids from server for given id-s
                ConcurrentDictionary<string, string> allGuids = Program.webClient.GetAllAlarmGuids(laneids);

                //should select only guids which I dont own
                ConcurrentDictionary<string, string> requiredAlarms = new ConcurrentDictionary<string, string>();    

                //check each of my guids whether it exists in the collection returned by the server
                    foreach (var item in alarmGuidLaneIdDict)
                {
                    if (!allGuids.ContainsKey(item.Key))
                    {

                    //if not exists at serverside then I remove it from my collection
                        alarmGuidLaneIdDict.TryRemove(item.Key, out string tempLaneId);
                        Debug.WriteLine("Item removed from list, because it is not present on the server.");

                        //now lets find the given nonexisting element in my DataGridWiew
                        int rowIdx = 0; int foundIdx = -1; bool found = false;
                        while (rowIdx < myDataGridView.RowCount && !found)
                        {
                            if (found = (myDataGridView.Rows[rowIdx].Cells[6].Value as NetworkChannel.HttpMessages.FullSpeedInfo).guid == item.Key)
                            {
                                foundIdx = rowIdx;
                            }
                            rowIdx++;
                        }
                        if (found)
                        {                                
                            myDataGridView.BeginInvoke(new MethodInvoker(() =>
                            {
                                //sometimes argument out of range exception, eg foundIdx = 2 when myDataGridView.Rows.Count = 2
                                myDataGridView.Rows.RemoveAt(foundIdx);
                            }));
                        }
                    }
                }
                //for each element that exists at serverside but not at clientside
                foreach (var responseAlarm in allGuids)
                {
                    if (!alarmGuidLaneIdDict.ContainsKey(responseAlarm.Key))
                    {
                        requiredAlarms.TryAdd(responseAlarm.Key, responseAlarm.Value);
                        alarmGuidLaneIdDict.TryAdd(responseAlarm.Key, responseAlarm.Value);
                        Debug.WriteLine("Item added: " + responseAlarm.Key);
                    }
                }
               //completely getting only elements which exists at serverside
                RefreshAlarms(Program.webClient.GetAlarms(requiredAlarms));
            });
            task.Start();
        }
    }

и другой метод:

private void RefreshAlarms(List<NetworkChannel.HttpMessages.FullSpeedInfo> alarms)
        {

            if (myDataGridView.InvokeRequired)
            {
                myDataGridView.BeginInvoke(new MethodInvoker(() => { RefreshAlarms(alarms); }));
            }
            else
            {                
                foreach (NetworkChannel.HttpMessages.FullSpeedInfo alarm in alarms)
                {
                    //do nothing yet
                    OnSpeedingEvent(alarm);
                }

                if (alarms == null)
                    return;
                //adding each new alarm to the DataGridView, the last cell is the object itself
                for (int i = 0; i < alarms.Count; i++)
                {
                    if (alarms[i].alarmInfo == null || string.IsNullOrEmpty(alarms[i].guid))
                        continue;

                    myDataGridView.Rows.Add("Play", alarms[i].guid, alarms[i].timeStamp.ToString(), ((int)alarms[i].speed).ToString() + " km/h", alarms[i].laneInfo.location, alarms[i].GetPriority(), alarms[i]);

                    int rowCount = myDataGridView.Rows.Count;
                    if (alarms[i].GetPriority() == "Significant")
                        myDataGridView.Rows[rowCount - 1].DefaultCellStyle.BackColor = Color.Orange;
                    else if (alarms[i].GetPriority() == "Important")
                        myDataGridView.Rows[rowCount - 1].DefaultCellStyle.BackColor = Color.OrangeRed;
                    else if (alarms[i].GetPriority() == "Warning")
                        myDataGridView.Rows[rowCount - 1].DefaultCellStyle.BackColor = Color.Yellow;
                }
            }
        }

Я уже пытался очистить весь DataGridView в каждом тике и просто добавить каждый сигнал тревоги.сразу после этого, но затем он мигает и сбрасывает полосы прокрутки, делая его бесполезным.Любой совет, который помогает мне разрешить исключение или другие концепции, которые делают DataGridView современным, приветствуется.
Другая информация: Объекты тревоги содержат данные изображения.Количество объектов, хранящихся на сервере, варьируется, может вырасти от +5 новых сигналов тревоги / мин до +100 сигналов тревоги / минут, это почти случайно.Клиент может удалить любой выбранный элемент из DataGridView.

Ответы [ 2 ]

0 голосов
/ 30 ноября 2018

Увеличение интервала тиков мне не помогло.Но я нашел точную причину исключения и решение.Когда я звонил

myDataGridView.BeginInvoke(new MethodInvoker(() =>
                            {                                
                                myDataGridView.Rows.RemoveAt(foundIdx);
                            }));

несколько раз, то порядок и время их выполнения были неопределенными, потому что это асинхронные вызовы.После того, как я заменил myDataGridView.BeginInvoke (...) на myDataGridView.Invoke (...), исключений больше не было.

Спасибо, что указали в правильном направлении:)

0 голосов
/ 30 ноября 2018

Существует множество потоков, работающих с этими элементами управления, между использованием Task.Start и затем BeginInvoke при удалении элементов, в котором также используется отдельный поток.

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

При возникновении подобных проблем с потоками очень часто они возникают непоследовательно.Это может объяснить, почему ошибка возникает «иногда».

Один из способов исправить это - убедиться, что вы не выполняете ни одну из этих трех вещей одновременно:

  • Добавление строк
  • Цикл по строкам и выяснение, какие из них удалить
  • Удаление строк

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

Кроме того, вместо удаления строк, когда вы найдете строки для удаления, создайте список строк, а когда закончите, удалите их все сразу, стараясь всегда удалятьих в порядке убывания.В противном случае, если вы попытаетесь удалить строку 10, за которой следует строка 11, после удаления строки 10 строка, которая была в 11, теперь равна 10. Теперь, если вы удаляете 11, вы удаляете ту, которая была в 12. Или нет 11 иВы снова получаете исключение.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...