MemoryStream have one thread write to it and another read
Asked Answered
C

2

20

This is how I write to a stream then read from it using 1 thread:

        System.IO.MemoryStream ms = new System.IO.MemoryStream();

        // write to it
        ms.Write(new byte[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 7);

        // go to the begining
        ms.Seek(0, System.IO.SeekOrigin.Begin);

        // now read from it
        byte[] myBuffer = new byte[7];
        ms.Read(myBuffer, 0, 7);

Now I was wondering if it is possible to write to the memory-stream from one thread and read that stream from a separate thread.

Ceasefire answered 8/9, 2012 at 5:20 Comment(2)
Read this #2825666Antimonic
Yeah lot's of places they talk about it. I just will appreciate if I could see a very basic example of how I will be able to do it...Ceasefire
S
20

You can't use a Stream with seeking capabilities from 2 threads simultaneous since a Stream is state full. e.g. A NetworkStream has 2 channels, one for reading and one for writing and therefore can't support seeking.

If you need seeking capabilities, you need to create 2 streams, one for reading and one for writing respectively. Else you can simply create a new Stream type which allows reading and writing from a underlying memory stream by taking exclusive access to the underlying stream and restore its write/read position. A primitive example of that would be:

class ProducerConsumerStream : Stream
{
    private readonly MemoryStream innerStream;
    private long readPosition;
    private long writePosition;

    public ProducerConsumerStream()
    {
        innerStream = new MemoryStream();
    }

    public override bool CanRead { get { return true;  } }

    public override bool CanSeek { get { return false; } }

    public override bool CanWrite { get { return true; } }

    public override void Flush()
    {
        lock (innerStream)
        {
            innerStream.Flush();
        }
    }

    public override long Length
    {
        get 
        {
            lock (innerStream)
            {
                return innerStream.Length;
            }
        }
    }

    public override long Position
    {
        get { throw new NotSupportedException(); }
        set { throw new NotSupportedException(); }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        lock (innerStream)
        {
            innerStream.Position = readPosition;
            int red = innerStream.Read(buffer, offset, count);
            readPosition = innerStream.Position;

            return red;
        }
    }

    public override long Seek(long offset, SeekOrigin origin)
    {
        throw new NotSupportedException();
    }

    public override void SetLength(long value)
    {
        throw new NotImplementedException();
    }

    public override void Write(byte[] buffer, int offset, int count)
    {
        lock (innerStream)
        {
            innerStream.Position = writePosition;
            innerStream.Write(buffer, offset, count);
            writePosition = innerStream.Position;
        }
    }
}
Sueannsuede answered 8/9, 2012 at 5:34 Comment(5)
It works great thanks! how should I implement the seek method? should I just move the innerStream.Position ?Ceasefire
Think about it. What do you want seek to do? seek in the read channel or seek in the write channel? or both?Sueannsuede
@Sueannsuede What do I need to implement the topic title ("MemoryStream have one thread write to it and another read") but with strings and not bytes and without any seeking ?Conjunctive
What happens when the Consumer out paces the Producer? I believe it will cause the Consumer to get an End-of-Stream prematurely.Payload
@NickWhaley Good point, this implementation does not take care of that. The higher order producer should signal the higher order consumer when data is available. Feel free to post an implementation that does this for youSueannsuede
S
1

If anyone is still looking for solutions to this, code below is quite simple and seems to be working fine for simultaneous reading/writing from two different threads.

public class RWMemoryStream : MemoryStream
{
    private long readPosition;
    private long writePosition;

    private readonly object lockStream = new object();

    public override void Write(byte[] buffer, int offset, int count)
    {
        lock (lockStream)
        {
            Position = writePosition;
            base.Write(buffer, offset, count);
            writePosition = Position;
        }
    }

    public override int Read(byte[] buffer, int offset, int count)
    {
        int result;
        
        lock (lockStream)
        {
            Position = readPosition;
            result = base.Read(buffer, offset, count);
            readPosition += result;
        }

        return result;
    }
}
Sommersommers answered 7/4, 2023 at 10:55 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Cufic

© 2022 - 2024 — McMap. All rights reserved.