.NETにブロッキングStreamReader、TextReader、またはStringReaderはありますか?
質問
TextReaderから入力を読み取り、一度に1行ずつ処理するバックグラウンドタスクを実行したい。ユーザーがフィールドにテキストを入力し、送信ボタンをクリックするまで、バックグラウンドタスクをブロックする必要があります。テキストが利用可能になるまでブロックし、何らかの方法で基礎となるソースにさらにテキストを追加できるTextReaderのフレーバーはありますか?
同じMemoryStreamを指すStreamReaderとStreamWriterが動作する可能性があると考えましたが、動作しないようです。 StreamReaderは、開始時にMemoryStreamが空であることを認識し、再びチェックすることはありません。
ProcessLine()メソッドを記述して、ユーザーが送信ボタンをクリックするたびに呼び出す方が簡単だと思います。ただし、プラグインアーキテクチャを設計しようとしています。プラグインを、入力ストリームと出力ストリームを備えた昔ながらのコンソールアプリのように見せたいです。ユーザーが入力テキストのある送信ボタンをクリックするまで、プラグインの入力ストリームをブロックするだけです。
解決
これは実装されていないようです-これは奇妙なことです、なぜなら私はそれが有用なコンストラクトになることに同意するからです。しかし、書くのは簡単なはずです。このような何かが動作するはずです:
public class BlockingStream: Stream
{
private readonly Stream _stream;
public BlockingStream(Stream stream)
{
if(!stream.CanSeek)
throw new ArgumentException("Stream must support seek", "stream");
_stream = stream;
}
public override void Flush()
{
lock (_stream)
{
_stream.Flush();
Monitor.Pulse(_stream);
}
}
public override long Seek(long offset, SeekOrigin origin)
{
lock (_stream)
{
long res = _stream.Seek(offset, origin);
Monitor.Pulse(_stream);
return res;
}
}
public override void SetLength(long value)
{
lock (_stream)
{
_stream.SetLength(value);
Monitor.Pulse(_stream);
}
}
public override int Read(byte[] buffer, int offset, int count)
{
lock (_stream)
{
do
{
int read = _stream.Read(buffer, offset, count);
if (read > 0)
return read;
Monitor.Wait(_stream);
} while (true);
}
}
public override void Write(byte[] buffer, int offset, int count)
{
lock (_stream)
{
long currentPosition = _stream.Position;
_stream.Position = _stream.Length;
_stream.Write(buffer, offset, count);
_stream.Position = currentPosition;
Monitor.Pulse(_stream);
}
}
public override bool CanRead
{
get
{
lock (_stream)
{
return _stream.CanRead;
}
}
}
public override bool CanSeek
{
get
{
lock (_stream)
{
return _stream.CanSeek;
}
}
}
public override bool CanWrite
{
get
{
lock (_stream)
{
return _stream.CanWrite;
}
}
}
public override long Length
{
get
{
lock (_stream)
{
return _stream.Length;
}
}
}
public override long Position
{
get
{
lock (_stream)
{
return _stream.Position;
}
}
set
{
lock (_stream)
{
_stream.Position = value;
Monitor.Pulse(_stream);
}
}
}
}
他のヒント
メインアプリケーションで、ユーザーが[送信]をクリックしたときに発生するイベントを作成する方がはるかに良いと思います。テキストデータはイベント引数で渡されます。各プラグインは、イベントのイベントハンドラーを登録し、イベントが発生したときに渡されるデータを処理します。これにより、多くのプラグインがあなたの側で多くの配管作業をすることなく、単一の送信からデータを処理できるようになり、イベントが発生するまでプラグインがただアイドル状態にできることを意味します。