简体   繁体   中英

How to sync between a listening/sending tcp client thread and the main execution?

i have a simple windows service which runs and starts a thread which listen/receive heartbeat via tcp/ip. i'm having a hard time finding ways to sync between getting information from the tcp thread and using that value to update something in the main thread.

i try to use a thread.sleep method and keep on looping it for a few times while awaiting the answer back from the thread and then getting the value, but that method seems to be a bit volatile with the method sometimes working and sometimes not.

so what's a good way to sync between these two? basically what i want to do is to start the listening tcp thread, get specific value and the update the main program.

attached are the receive function and the function which i used to start the thread. ps: i'm a totally noobie when it comes to tcp/ip and c# so any comments on any part of the code or the design is more than welcome :)

public virtual void Receive()
        {
            string eventMessage = string.Empty;
            int bytesRcvd = 0;
            int totalBytesRcvd = 0;
            byte[] byteBuffer = new byte[maxBufferSize];
            NetworkStream listenStream;
            try
            {
                if (client.Connected)
                {
                    listenStream = client.GetStream();    
                }
                else
                {
                    return;
                }

                while (true)
                {                
                    //message that is slot in from the object will get sent here.
                    if (!string.IsNullOrEmpty(MessageToSend))
                    {
                        Send(MessageToSend);
                        MessageToSend = string.Empty;
                    }

                    // must convert it back and look for the delimiter, cannot wait for the three heartbeat to pass
                    string leftoverMsg = string.Empty;

                    bytesRcvd = listenStream.Read(byteBuffer, totalBytesRcvd, maxBufferSize - totalBytesRcvd);
                    totalBytesRcvd += bytesRcvd;

                    //if more than heart beat size, can process to see if it's a heartbeat and proceed to send
                    if (totalBytesRcvd > msgHeartbeatSize)
                    {
                        eventMessage = Encoding.ASCII.GetString(byteBuffer, 0, totalBytesRcvd);
                        ProcessMessage(eventMessage, ref leftoverMsg, ref totalBytesRcvd, ref byteBuffer);
                    }
                }
            }
            catch (ThreadAbortException thEx)
            {
                //do nothing as main thread has aborted and waiting to close
                logger.Info(Thread.CurrentThread.Name + " is stopped. ");
            }
            catch (Exception exce)
            {
                bIsActive = false;
                logger.Error(exce);
                CleanUp();
            }
            finally
            {
                logger.Info(String.Format("Thread {0} Exiting. ", Thread.CurrentThread.Name));
            }
        }

public virtual void StartReceivingThread()
        {
            Thread thrReceive = new Thread(Receive);
            try
            {
                if (!bIsActive && Connect())
                {
                    //NOTE: exception thrown by a thread can only be captured by that thread itself
                    //start a listen thread
                    //wait until heartbeat message is accepted

                    thrReceive.Name = "thr" + serviceType.Name;
                    thrReceive.Start();
                    bIsActive = true;

                    //wait to get the heartbeat message
                    for (int i = 0; i < maxRetry; i++)
                    {
                        Thread.Sleep(maxTimeOutValue);
                        if (bIsReceivingHeartbeat)
                            break;
                    }
                    //if nothing happens close the connection and try again
                    if (!bIsReceivingHeartbeat)
                    {
                        bIsActive = false;
                        CleanUp();
                        logger.Info("Closing  receiver thread - " + thrReceive.Name);
                    }
                    else
                    {
                        logger.Info("Starting  receiver thread - " + thrReceive.Name);
                    }
                }


            }
            catch(Exception ex)
            {
                logger.Error(ex);
            }
            //finally
            //{
            //    logger.Info("Exiting  receiver thread - " + thrReceive.Name);
            //}
        }

I assume bIsReceivingHeartbeat is a bool member variable of the class. If the value changed in one thread (receiver) is not visible in the other thread this is most likely due to memory barrier. I am saying this from my Java background but this is most likely true in .net as well.

Try declaring the variables volatile or use a property and make the getter and setter synchronized:

private bool bIsReceivingHeartbeat;
public bool IsReceivingHeartbeat
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    get { return bIsReceivingHeartbeat; }
    [MethodImpl(MethodImplOptions.Synchronized)]
    set { bIsReceivingHeartbeat = value; }
}

And in the calling code:

if (!IsReceivingHeartbeat) ....

I am writing from Java background but the situation most likely similar

(Looks like you also posted this code in refactormycode.com.)

Anyway, instead of the loop with a sleep delay, I recommend using an Event object that pulsed by the code that sets IsReceivingHeartbeat. See the ManualResetEvent and AutoResetEvent classes in MSDN.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM