进行进程间通信的一种方法是通过进程之间的(命名)管道。
我希望在两个线程之间实现相同的“队列”式通信。生产者应该编写一个文本库命令(使用TextWriter或输出Stream)。 消费者应该从TextReader中读取。因为,你考虑一下,OutputStream / -Writer是InputStream / -Reader硬币的另一面。因此,使用Writer来为Reader填充数据,理论上应该很容易。
(这里的标准方法是在线程之间有一个队列。但是我希望使用TextReader和TextWriter,因为我已经有了前端和后端的代码。这样通过连接控制台就可以轻松调试。在/ Console.Out到生产者/消费者。)
我认为将作者连接到阅读器真的很容易,但我找不到如何做到这一点。
我可以自己写一个这样的连接,但感觉它“应该”已经存在。
有什么想法吗?
干杯 雷夫
答案 0 :(得分:2)
我不鼓励使用流和TextWriter / TextReader作为线程之间的有效通信方式。您需要为每个“队列”设置一个流,并确保完全写入或读取有效数据,您需要为每次写入或读取操作锁定该流。一个更好的解决方案可能是这样的:
设置一个类型字符串的队列,以及一对手动重置动作。一般的想法是使用线程信令允许两个线程进行通信而无需锁定。
public static class ThreadTest
{
public void Main()
{
long exit = 0;
Queue<string> messages = new Queue<string>();
ManualResetEvent signal1 = new ManualResetEvent();
ManualResetEvent signal2 = new ManualResetEvent();
signal2.Set();
Thread writer = new Thread(() =>
{
while (exit == 0)
{
string value = Console.ReadLine();
if (value == "exit")
{
Interlocked.Exchange(ref exit, 1);
}
else
{
messages.Enqueue(value);
Console.WriteLine("Written: " + value);
signal1.Set();
}
signal2.WaitOne();
}
});
Thread reader = new Thread(() =>
{
while (exit == 0)
{
signal1.WaitOne();
signal2.Reset();
value = messages.Dequeue();
Console.WriteLine("Read: " + value);
signal2.Set();
signal1.Reset();
}
});
reader.Start();
writer.Start();
}
}
答案 1 :(得分:1)
我放弃了找到“现成的”解决方案。我写了自己的。 一个新的类ThroughputStream,它的Write-end接收数据,通过线程安全队列发送到Read-end,使用接收到的数据块来读取。
namespace My.IO
{
public class ThrouputStream
{
private InputStreamClass inputStream;
private OutputStreamClass outputStream;
private Queue<byte[]> queue = new Queue<byte[]>();
private System.Threading.EventWaitHandle queueEvent = new System.Threading.EventWaitHandle(false, System.Threading.EventResetMode.AutoReset);
public ThrouputStream()
{
inputStream = new InputStreamClass(this);
outputStream = new OutputStreamClass(this);
}
public Stream InputStream
{
get { return inputStream; }
}
public Stream OutputStream
{
get { return outputStream; }
}
private class InputStreamClass : Stream
{
private readonly Queue<byte[]> queue;
private readonly ThrouputStream parent;
private byte[] currentBlock = null;
private int currentBlockPos = 0;
private Boolean closed = false;
private int readTimeoutMs = System.Threading.Timeout.Infinite;
public InputStreamClass(ThrouputStream parent)
{
this.parent = parent;
this.queue = parent.queue;
}
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return false; }
}
public override void Flush()
{
// Do nothing, always flushes.
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public override bool CanTimeout
{
get
{
return true;
}
}
public override int ReadTimeout
{
get
{
return readTimeoutMs;
}
set
{
readTimeoutMs = value;
}
}
public override int Read(byte[] buffer, int offset, int count)
{
if (currentBlock == null)
{
int queueCount;
lock (queue)
{
queueCount = queue.Count;
if ( queueCount > 0 )
currentBlock = queue.Dequeue();
}
if (currentBlock == null && !parent.outputStream.IsClosed )
{
parent.queueEvent.WaitOne(readTimeoutMs);
lock (queue)
{
if (queue.Count == 0)
return 0;
currentBlock = queue.Dequeue();
}
}
currentBlockPos = 0;
}
if (currentBlock == null)
return 0;
int read = Math.Min(count, currentBlock.Length - currentBlockPos);
Array.Copy(currentBlock, currentBlockPos, buffer, offset, read);
currentBlockPos += read;
if (currentBlockPos == currentBlock.Length)
{
// did read whole block
currentBlockPos = 0;
currentBlock = null;
}
return read;
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotImplementedException();
}
public override void SetLength(long value)
{
throw new NotImplementedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
throw new NotImplementedException();
}
public override void Close()
{
this.closed = true;
base.Close();
}
}
private class OutputStreamClass : Stream
{
private bool isClosed = false;
private readonly Queue<byte[]> queue;
private readonly ThrouputStream parent;
public OutputStreamClass(ThrouputStream parent)
{
this.parent = parent;
this.queue = parent.queue;
}
public override bool CanRead
{
get { return false; }
}
public override bool CanSeek
{
get { return false; }
}
public override bool CanWrite
{
get { return true; }
}
public override void Flush()
{
// always flush
}
public override long Length
{
get { throw new NotSupportedException(); }
}
public override long Position
{
get
{
throw new NotSupportedException();
}
set
{
throw new NotSupportedException();
}
}
public override int Read(byte[] buffer, int offset, int count)
{
throw new NotSupportedException();
}
public override long Seek(long offset, SeekOrigin origin)
{
throw new NotSupportedException();
}
public override void SetLength(long value)
{
throw new NotSupportedException();
}
public override void Write(byte[] buffer, int offset, int count)
{
byte[] copy = new byte[count];
Array.Copy(buffer, offset, copy, 0, count);
lock (queue)
{
queue.Enqueue(copy);
try
{
parent.queueEvent.Set();
}
catch (Exception)
{ }
}
}
public override void Close()
{
this.isClosed = true;
base.Close();
// Signal event, to stop waiting consumer
try
{
parent.queueEvent.Set();
}
catch (Exception)
{ }
}
public bool IsClosed
{
get { return isClosed; }
}
}
}
}