2009-09-25 2 views
8

은 내가이 인터페이스를 구현하는 객체 제공 필요로하는 라이브러리를 사용하고 있음을 메모리 스트림에서 :인가 거기에 파일 스트림과 같은 블록

public interface IConsole { 
    TextWriter StandardInput { get; } 
    TextReader StandardOutput { get; } 
    TextReader StandardError { get; } 
} 

개체의 독자는 다음 라이브러리에 의해 익숙해를 :

IConsole console = new MyConsole(); 
int readBytes = console.StandardOutput.Read(buffer, 0, buffer.Length); 

일반적으로 IConsole을 구현하는 클래스는 외부 프로세스에서 온 것으로 StandardOutput 스트림을 가지고 있습니다. 이 경우 console.StandardOutput.Read 호출은 StandardOutput 스트림에 기록 된 데이터가있을 때까지 블로킹하여 작동합니다.

내가하려고하는 것은 MemoryStreams를 사용하는 Test IConsole 구현을 만들고 StandardInput에 StandardInput에 나타나는 모든 것을 echo하는 것입니다. 나는 시도 :

MemoryStream echoOutStream = new MemoryStream(); 
StandardOutput = new StreamReader(echoOutStream); 

그러나 문제는 일부 데이터가 될 때까지 console.StandardOutput.Read 블록보다는 0을 반환합니다. 어쨌든 사용할 수있는 데이터가 없거나 사용할 수있는 메모리 스트림이 다른 경우 차단할 MemoryStream을 얻을 수 있습니까?

+1

출력 스트림에서 실제로 읽지 않아야합니다. –

답변

7

결국 MemoryStream을 상속하고 읽기 및 쓰기 메서드를 인계 받아 쉽게 수행 할 수있는 방법을 발견했습니다. 연속적으로 기록 할 수있는없이, 버전 당신이 쓰기에 따라 스트림을 읽어야와

public class EchoStream : MemoryStream 
{ 
    private readonly ManualResetEvent _DataReady = new ManualResetEvent(false); 
    private readonly ConcurrentQueue<byte[]> _Buffers = new ConcurrentQueue<byte[]>(); 

    public bool DataAvailable{get { return !_Buffers.IsEmpty; }} 

    public override void Write(byte[] buffer, int offset, int count) 
    { 
     _Buffers.Enqueue(buffer); 
     _DataReady.Set(); 
    } 

    public override int Read(byte[] buffer, int offset, int count) 
    { 
     _DataReady.WaitOne(); 

     byte[] lBuffer; 

     if (!_Buffers.TryDequeue(out lBuffer)) 
     { 
      _DataReady.Reset(); 
      return -1; 
     } 

     if (!DataAvailable) 
      _DataReady.Reset(); 

     Array.Copy(lBuffer, buffer, lBuffer.Length); 
     return lBuffer.Length; 
    } 
} 

: 당신의 대답에 영감을

public class EchoStream : MemoryStream { 

    private ManualResetEvent m_dataReady = new ManualResetEvent(false); 
    private byte[] m_buffer; 
    private int m_offset; 
    private int m_count; 

    public override void Write(byte[] buffer, int offset, int count) { 
     m_buffer = buffer; 
     m_offset = offset; 
     m_count = count; 
     m_dataReady.Set(); 
    } 

    public override int Read(byte[] buffer, int offset, int count) { 
     if (m_buffer == null) { 
      // Block until the stream has some more data. 
      m_dataReady.Reset(); 
      m_dataReady.WaitOne();  
     } 

     Buffer.BlockCopy(m_buffer, m_offset, buffer, offset, (count < m_count) ? count : m_count); 
     m_buffer = null; 
     return (count < m_count) ? count : m_count; 
    } 
} 
+1

'Read()'에 경쟁 조건이 있습니다. '쓰기()가'널 버퍼의 확인 및'm_dataReady.Reset() '사이에 다른 스레드에 의해 호출되는 경우 잠재적으로 서버가 다시 데이터를 전송하지 않을 경우 영원히 기다려야 할 수도 있습니다. 대부분의 요청/응답 프로토콜에서 이는 데드 록을 생성합니다. 대신 자동 이벤트를 사용하는 것이 좋습니다. –

+0

충분합니다. 나는 체크인할만한 가치가 있다고 동의한다. 말할 것도없이 위의 코드를 5 년 동안 SSH 서비스를 직면하고있는 공공 장소에서 생산 한 상태 였고 서비스가 중단되지 않았으므로 매우 낮은 확률로 의심됩니다. – sipwiz

+0

@sipwiz 이것은 좋은 대답입니다. 그러나 Array.copy를 사용하면 읽기 기능이 제대로 작동하지 않습니다. 오프셋 된 복사본을 지원하지 않습니다. 으로 변경해야합니다. ** Buffer.BlockCopy (m_buffer, 0, buffer, offset, m_count); ** 이것은 또한 대부분의 시스템에서 더 빠릅니다. –

8

, 여기 내 다중 스레드, 다중 쓰기 버전입니다. 제 버전은 ConcurrentQueue에 서면 버퍼를 버퍼링합니다 (간단한 큐로 변경하고 잠그는 것이 매우 간단합니다)

+0

그러나 이것은 Write 메서드의 버그입니다. _Buffers.Enqueue (버퍼);는'_Buffers.Enqueue (buffer.Take (count) .ToArray()); '로 대체되어야합니다. 스레드 간 데이터 차단 및 교환! 감사! –

관련 문제