Search code examples
c#xmlserializerbinaryformatter

BinaryFormatter.Deserialize hangs the whole thread


I have two simple applications connected via named pipes. In the client side I have a method that checks incoming messages every n ms:

private void timer_Elapsed(Object sender, ElapsedEventArgs e)
{
      IFormatter f = new BinaryFormatter();
      try
      {
           object temp = f.Deserialize(pipeClient); //hangs here
           result = (Func<T>)temp;
      }
      catch
      {
      }
}

In the beginning the pipe is empty, and f.Deserialize method hangs the whole application. And I can't even check that pipe's empty? Is there any solution to this problem?

UPD: tried XmlSerializer, everything's the same.


Solution

  • The thing that is hanging on you is the pipeClient.Read( call both formatters are making internally.

    This is the expected behavior of a Stream, when you call Read:

    Return Value
    Type: System.Int32
    The total number of bytes that are read into buffer. This might be less than the number of bytes requested if that number of bytes is not currently available, or 0 if the end of the stream is reached.

    So the stream will block till data shows up or throw a timeout exception if it is the type of stream that supports timeouts. It will never just return without reading anything unless you are "at the end of the stream" which for a PipeStream (or similarly a NetworkStream) only happens when the connection is closed.

    The way you solve the problem is don't use a timer to check if a new message arrives, just start up a background thread and have it sitting in a loop, it will block itself until a message shows up.

    class YourClass
    {
        public YourClass(PipeStream pipeClient)
        {
            _pipeClient = pipeClient;
            var task = new Task(MessageHandler, TaskCreationOptions.LongRunning);
            task.Start();
        }
    
        //SNIP...
    
        private void MessageHandler()
        {
            while(_pipeClient.IsConnected)
            {
                IFormatter f = new BinaryFormatter();
                try
                {
                     object temp = f.Deserialize(_pipeClient);
                     result = (Func<T>)temp;
                }
                catch
                {
                    //You really should do some kind of logging.
                }
            }
        }
    }