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#, чтобы сделать весь код еще более читабельным и компонованным.