C# форма замедляется из-за waitOne

У меня есть небольшая проблема, пытаясь закодировать устройство с помощью JSON.

Мое устройство на устройство работает хорошо, я использую его для отправки JSON на другое устройство через самодельный D2D-сервер. Таким образом, один из моих клиентов отправляет запрос другому клиенту с именем "сервер", этот сервер выполняет некоторые действия (выполняет некоторые запросы на сервере Asterisk), все в порядке с этим.

Но я столкнулся с проблемой: когда мой клиент отправляет запрос на мой сервер, я жду ответа от сервера с waitOne на autoResetEvent. В зависимости от запроса, моя форма будет отображать всплывающее окно. Но моя форма очень медленно показывает всплывающее окно, я думаю, что это связано с этим waitOne, я могу отправить как 10 запросов менее чем за 3 секунды, поэтому я думаю, что моя форма замедляется из-за этого.

По сути, я использую поток, который читает данные в сокете, когда мои данные завершены (весь Json извлекается), я отправляю событие, затем я должен освободить поток чтения, поэтому я делаю "BeginInvoke", поэтому моя форма будет обрабатывать событие:

 _sync.BeginInvoke(ChannelChange, new Object[] {this, ec});

После показа всплывающего окна моя форма обрабатывает несколько других событий, она выполняет некоторый запрос к серверу (поэтому поток останавливается во время множественного запроса).

Я думаю, поэтому мое всплывающее окно отображается очень медленно. Итак, мой вопрос: как я могу избежать этого замедления?

Небольшое резюме:

MyForm использует Client, этот клиент содержит некоторый метод, например "retrieveChannelById". Когда мой поток чтения извлекает полный Json, он генерирует событие, вызывающее мою форму (если я не вызову событие с моей формой, мой поток чтения будет остановлен, и он худший) Моя форма выполняет какой-либо метод Client, такой как "retrieveChannelById" устанавливает поток для приостановки на некоторое время (обычно менее 1 секунды, но он выполняет много запросов довольно быстро).

Итак, я хотел бы знать, как я могу указать поток для выполнения этого метода, не останавливая поток формы?

Но все же, когда я занимаюсь событием, я делаю

Channel chan = Client.GetChannelByChanID(e.ChannelId);

Я действительно не знаю, как справиться с этим замедлением формы. Нужно ли выполнять этот метод в другом потоке? (Я думаю, это ничего не изменит, так как моя форма должна ждать, пока другой поток установит канал правильно?). Мне нужно решение, поэтому мой поток форм не установлен в WaitOne, я думаю, что это моя проблема.

Это действительно сложно объяснить, на самом деле...

Вот мой код для метода retrieveChannelById:

   SendD2DMessage("*", "server", "{\"classname\":\"GetChannelByChanID\",\"chanID\":\"" + chanID + "\"}");
        waitingData = "reponseGetChannelByID";           
        if (this._messageRecieved.WaitOne(5000))
        {

            try
            { 
                if (dataRecieved == "null")
                {
                    Console.WriteLine("But data null");
                    return null;
                }
                else
                {
                    Channel chan = JsonConvert.DeserializeObject<Channel>(dataRecieved);
                    Console.WriteLine("Channel received : " + chan.Extension);
                    return chan;
                }
            }
            catch (Exception ex)
            {
                SynsipClientLog.Log("Error while deserializing Channel Object in GetChannelByID Method :" + ex.Message);
                return null;
            }
        }
        else
        {
            waitingData = "";
            Console.WriteLine("Channel NOT RECEIVED");
            SynsipClientLog.Log("No response from D2DConsole in GetChannelByChanId method");
            return null;
        }

Вот код ChannelChange, этот код в моей основной форме.

  void SynsipCli_ChannelChange(object sender, SynsipRemoteChannelChangeEventsArgs e)
    {
        try
        {
            //Console.WriteLine("test ChannelChange");
            Channel chan = SynsipCli.GetChannelByChanID(e.ChannelId);
            Channel linkedChan = null;
            if (chan != null && chan.LinkedChannelID.Length>1)
            {
                linkedChan = SynsipCli.GetChannelByChanID(chan.LinkedChannelID);
            }

            // alertes pour les trunck
            if (e.Channelcategory == ChannelCategory.Trunck && !MenuDoNotDisplay.Checked)
            {
                bool isGeneralNumber = false;

                if (chan != null)
                {
                    foreach (string str in SynsipCli.AstParameters.GeneralNumbers)
                    {
                        if (chan.CallerIDNum.Contains(str) || chan.LinkedCallerID.Contains(str) || chan.DNIS.Contains(str) || chan.Extension.Contains(str))
                            isGeneralNumber = true;
                    }

                    if (chan.State != ChannelState.Down && chan.State != ChannelState.Hangup && chan.State != ChannelState.Unknow && (chan.LinkedCallerID == Extension || chan.CallerIDNum == Extension || isGeneralNumber))
                    {

                        LaunchAlert(chan);
                    }

                }
            }
            else if (linkedChan != null && linkedChan.ChannelCategory == ChannelCategory.Trunck)
            {
                bool isGeneralNumber = false;

                if (linkedChan != null)
                {
                    foreach (string str in SynsipCli.AstParameters.GeneralNumbers)
                    {
                        if (linkedChan.CallerIDNum.Contains(str) || linkedChan.LinkedCallerID.Contains(str) || linkedChan.DNIS.Contains(str))
                            isGeneralNumber = true;
                    }

                    if (linkedChan.State != ChannelState.Down && linkedChan.State != ChannelState.Hangup && linkedChan.State != ChannelState.Unknow && (linkedChan.LinkedCallerID == Extension || linkedChan.CallerIDNum == Extension || isGeneralNumber))
                    {
                        LaunchAlert(linkedChan);

                        //LaunchAlert(tchan);

                    }

                }

            }
            if (((chan != null && (chan.CallerIDNum == txtExtention.Text || chan.LinkedCallerID == txtExtention.Text)) || e.Extension == txtExtention.Text) && !MenuDoNotDisplay.Checked) // alertes pour le local
            {
                string ext = SynsipCommunicator.Properties.Settings.Default.Extention;

                Extension exten = SynsipCli.GetExtension(ext);

                foreach (Channel tchan in exten.Channels)
                {
                    if (tchan != null && tchan.CalllType == CallType.Internal)
                    {
                        LaunchAlert(tchan);


                    }
                }
            }

            // suppression des alerts qui ne doivent plus exister
            for (int i = 0; i < Alerts.Count; i++)
            {
                Channel tchan = SynsipCli.GetChannelByChanID(Alerts[i].chanId);
                if (tchan == null)
                    Alerts[i].Close();
                else if (tchan.State == ChannelState.Down || tchan.State == ChannelState.Hangup || tchan.State == ChannelState.Unknow)
                    Alerts[i].Close();
            }
        }
        catch (Exception exe)
        {
            //Console.WriteLine("ERREUR CHANNEL CHANGE " + exe.Message);
        }

    }

Метод LaunchAlert просто проверяет некоторые вещи в моем объекте Channel, создает кадр и затем показывает кадр:

 if (!MenuDoNotDisplay.Checked)
            {
                ShowWindow(frma.Handle, 4); //Pour éviter de prendre le focus
                SetWindowPos(frma.Handle, -1, frma.Location.X, frma.Location.Y, frma.Width, frma.Height, 0x0010);
            }

1 ответ

Не делайте никаких блокирующих ожиданий (например, event.WaitOne()) в потоке пользовательского интерфейса. Это приведет именно к описанному вами мёртвому поведению, потому что пользовательский интерфейс не может обновляться в течение этого периода времени.

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

Если вы хотите тайм-аут, как ваши 5, то запустите операцию отправки асинхронно и запустите таймер в пользовательском интерфейсе асинхронно параллельно. Оба должны закончить обратно в потоке пользовательского интерфейса. Затем посмотрите, закончится ли ваш таймер первым или вы сначала получите результат операции и справитесь с этой ситуацией.

Вы также можете обернуть свою операцию отправки в объект задачи C# TPL или использовать функции асинхронного / ожидающего выполнения C#, чтобы сделать весь код еще более читабельным и компонованным.

Другие вопросы по тегам