2009-09-25 16 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流。

我想要做的是創建一個測試IConsole實現,使用MemoryStreams和echo的任何出現在StandardInput上的回到StandardInput。我想:

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

但是與該問題是console.StandardOutput.Read將返回0,而不是塊,直到有一些數據。無論如何,我可以得到一個MemoryStream來阻止,如果沒有可用的數據或者我可以使用的內存流中有不同的?

+1

您確實不應該從輸出流中讀取數據。 – 2009-09-25 06:39:59

回答

7

最後,我發現了一個簡單的方法來從MemoryStream繼承並接管Read和Write方法。

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()'中有競爭條件。如果Write()由另一個線程在緩衝區空檢查和m_dataReady.Reset()之間調用,那麼如果服務器不再發送數據,您可能需要等待一段時間。在大多數請求/響應協議中,這會造成死鎖。我建議你改用一個自動事件。 – 2014-12-09 11:40:59

+0

夠公平的。我同意這是值得的檢查。不用說,我已經在面向公衆的SSH服務上生產了上述代碼5年,並且從未將服務掛起,因此我懷疑它的可能性很低。 – sipwiz 2014-12-11 03:02:40

+0

@sipwiz 這是一個很好的答案。但是,使用Array.copy不允許讀取功能正常。它不會支持偏移副本。您需要更改爲 ** Buffer.BlockCopy(m_buffer,0,buffer,offset,m_count); ** 這在大多數系統上也會更快。 – 2015-10-01 03:23:21

8

通過你的回答啓發,這裏是我的多線程,多寫版本:

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; 
    } 
} 

隨着你的版本,你應該閱讀時寫的流,沒有任何連續寫是不可能的。我的版本在ConcurrentQueue中緩存任何寫入的緩衝區(將其更改爲簡單的隊列並將其鎖定非常簡單)

+0

這太棒了,但是Write方法有一個錯誤,'_Buffers.Enqueue(buffer);'應該替換爲_Buffers.Enqueue(buffer.Take(count).ToArray());'然後它真的工作,阻塞和交換線程之間的數據!謝謝! – 2018-02-23 10:47:12

相關問題