C# form slowing down because of waitOne

757 Views Asked by At

I have a little problem, trying to code device to device porgram using JSON.

My device to device is working well, I'm using it to send JSON to an other device throug a homemade D2D server. So one of my client is sending request to an other client named "server", that server is doing some stuff ( doing some request on an Asterisk server ), everything is ok with that.

But i'm facing a problem : When my client is sending request to my server, i'm waiting for the answer from the server with a waitOne on a autoResetEvent. Depending of the request, my form will show a pop-up. But my form is very slow to show popup, I think it's due to those waitOne, i can send like 10 request in less than 3 seconds, so i think my form is slow down cause of these.

Basically, I'm using a thread that's reading data on a socket, when my data is complete ( the entire Json is retrieve ), i'm sending an event, then I have to release the reading thread, so i'm doing a "BeginInvoke" so my form will handle the event :

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

After showing the popup, my form is handling multiple other events, it's doing some request to the server ( so the thread is stopped during the multiple request ).

SO I guess, it's why my pop up is showing very slowly. So my question is how can I avoid this slow down ?

A little resume :

MyForm is using a Client, that client contains some method , like "retrieveChannelById". When my reading thread is retrieving a complete Json, it's throwing an event invoking my form ( If I doesn't invoke the event with my form, my reading thread is gonna be stopped and it's worst ) My form is performing some Client method such as "retrieveChannelById" setting the thread to pause for a while ( usually less than 1 second, but it's doing a lot of request pretty fast ).

So I would like to know, how can I can specify a thread to execute those method without pausing the form thread ?

But still, when i'm handling an event, i'm doing

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

I really don't know how to handle this slowing down of the form, Do I have to execute those method in an other thread ? ( It won't change anything I guess, since my form has to wait for the other thread to get the channel right ? ). I need a solution so my form thread is'nt set on WaitOne, I think it's my issue.

It's really hard to explain, actually ...

here is my code for the method 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;
        }

Here is the ChannelChange code, this code is in my main form.

  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);
        }

    }

The LaunchAlert method is just checking some stuff in my Channel object, create a frame and then show the frame :

 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

There are 1 best solutions below

5
On

Don't do any blocking waits (like the event.WaitOne()) on the UI thread. This will lead exactly to to the dead behavior you are describing, because the UI can't update itself during this time period.

Instead you should work asynchronously: Only start the operation on UI thread and then immediatly return. When the operation finishes then change back to the UI thread (e.g. with BeginInvoke), evaluate the result and whether your operation is still necessary and update the UI according to the result of the operation.

If you want a timeout like your 5s then start the send operation asynchronously and start a timer in the UI asynchronously in parallel. Both should finish back in the UI thread. Then see whether your timer finishes first or you first receive the result of the operation and handle that situation.

You might also be able to wrap your Send operation in a C# TPL Task object or use the C# async/await features to make the whole code even a bit more readable and composable.