简体   繁体   English

如何在侦听/发送tcp客户端线程和主要执行之间进行同步?

[英]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. 我有一个简单的Windows服务,该服务运行并启动一个线程,该线程通过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. 我很难找到从tcp线程获取信息和使用该值更新主线程中的内容之间进行同步的方法。

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. 我尝试使用thread.sleep方法,并在等待它从线程返回答案然后获取值的同时继续循环几次,但是该方法似乎有些不稳定,有时该方法有时有效,而有时则无效。

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. 基本上我想做的是启动监听tcp线程,获取特定值并更新主程序。

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 :) ps:在tcp / ip和c#方面,我完全是个傻瓜,因此对代码或设计的任何部分的任何评论都倍受欢迎:)

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. 我假设bIsReceivingHeartbeatbIsReceivingHeartbeatbool成员变量。 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. 我是从Java背景讲这个的,但这在.net中也很可能也是这样。

Try declaring the variables volatile or use a property and make the getter and setter synchronized: 尝试将变量声明为volatile或使用属性,并使getter和setter同步:

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 我是从Java背景撰写的,但情况很可能类似

(Looks like you also posted this code in refactormycode.com.) (看起来您也将此代码发布在了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. 无论如何,我建议您使用一个由设置IsReceivingHeartbeat的代码产生脉冲的Event对象,而不是使用具有睡眠延迟的循环。 See the ManualResetEvent and AutoResetEvent classes in MSDN. 请参阅MSDN中的ManualResetEvent和AutoResetEvent类。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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