.NET 异步流读/写 [英] .NET Asynchronous stream read/write
问题描述
我一直在尝试解决这个并发编程"考试练习(在 C# 中):
I have been trying to solve this "Concurrent Programming" exam exercise (in C#):
知道 Stream
类包含 int Read(byte[] buffer, int offset, int size)
和 void Write(byte[] buffer, int offset), int size)
方法,在 C# 中实现了 NetToFile
方法,该方法将从 NetworkStream net
实例接收到的所有数据复制到 FileStream 文件
实例.要进行传输,请使用异步读取和同步写入,避免在读取操作期间阻塞一个线程.当net
读操作返回值0时,传输结束.为简化起见,不需要支持操作的受控取消.
Knowing that
Stream
class containsint Read(byte[] buffer, int offset, int size)
andvoid Write(byte[] buffer, int offset, int size)
methods, implement in C# theNetToFile
method that copies all data received fromNetworkStream net
instance to theFileStream file
instance. To do the transfer, use asynchronous reads and synchronous writes, avoiding one thread to be blocked during read operations. The transfer ends when thenet
read operation returns value 0. To simplify, it is not necessary to support controlled cancel of the operation.
void NetToFile(NetworkStream net, FileStream file);
我一直在尝试解决这个练习,但我正在为一个与问题本身相关的问题而苦苦挣扎.但首先,这是我的代码:
I've been trying to solve this exercise, but I'm struggling with a question related with the question itself. But first, here is my code:
public static void NetToFile(NetworkStream net, FileStream file) {
byte[] buffer = new byte[4096]; // buffer with 4 kB dimension
int offset = 0; // read/write offset
int nBytesRead = 0; // number of bytes read on each cycle
IAsyncResult ar;
do {
// read partial content of net (asynchronously)
ar = net.BeginRead(buffer,offset,buffer.Length,null,null);
// wait until read is completed
ar.AsyncWaitHandle.WaitOne();
// get number of bytes read on each cycle
nBytesRead = net.EndRead(ar);
// write partial content to file (synchronously)
fs.Write(buffer,offset,nBytesRead);
// update offset
offset += nBytesRead;
}
while( nBytesRead > 0);
}
我的问题是,在问题陈述中,是这样说的:
The question I have is that, in the question statement, is said:
要进行传输,请使用异步读取和同步写入,避免一个线程在读取过程中被阻塞操作
To do the transfer, use asynchronous reads and synchronous writes, avoiding one thread to be blocked during read operations
我不确定我的解决方案是否能完成本练习中的要求,因为我使用 AsyncWaitHandle.WaitOne()
等待异步读取完成.
I'm not really sure if my solution accomplishes what is wanted in this exercise, because I'm using AsyncWaitHandle.WaitOne()
to wait until the asynchronous read completes.
另一方面,我并没有真正弄清楚在这种情况下什么是非阻塞"解决方案,因为 FileStream
写入是同步进行的.. 要做到这一点,我必须等到 NetworkStream
读取完成才能继续编写 FileStream
,不是吗?
On the other side, I'm not really figuring out what is meant to be a "non-blocking" solution in this scenario, as the FileStream
write is meant to be made synchronously... and to do that, I have to wait until NetworkStream
read completes to proceed with the FileStream
writing, isn't it?
你能帮我解决这个问题吗?
Can you, please, help me out with this?
[ EDIT 1 ] 使用回调解决方案
[ EDIT 1 ] Using callback solution
好的,如果我理解 Mitchel Sellers 和 willvv 回答说,有人建议我使用回调方法将其转变为非阻塞"解决方案.这是我的代码,然后:
Ok, if I understood what Mitchel Sellers and willvv replied, I've been counseled to use a callback method to turn this into a "non-blocking" solution. Here is my code, then:
byte[] buffer; // buffer
public static void NetToFile(NetworkStream net, FileStream file) {
// buffer with same dimension as file stream data
buffer = new byte[file.Length];
//start asynchronous read
net.BeginRead(buffer,0,buffer.Length,OnEndRead,net);
}
//asynchronous callback
static void OnEndRead(IAsyncResult ar) {
//NetworkStream retrieve
NetworkStream net = (NetworkStream) ar.IAsyncState;
//get number of bytes read
int nBytesRead = net.EndRead(ar);
//write content to file
//... and now, how do I write to FileStream instance without
//having its reference??
//fs.Write(buffer,0,nBytesRead);
}
正如您可能已经注意到的,我被困在回调方法上,因为我没有对 FileStream
实例的引用,我想在其中调用Write(...)"方法.
As you may have noticed, I'm stuck on the callback method, as I don't have a reference to the FileStream
instance where I want to invoke the "Write(...)" method.
此外,这不是线程安全的解决方案,因为 byte[]
字段是公开的,并且可能在并发的 NetToFile
调用之间共享.我不知道如何在不暴露外部作用域中的这个 byte[]
字段的情况下解决这个问题......而且我几乎可以肯定它可能不会以这种方式暴露.
Additionally, this is not a thread-safe solution, as the byte[]
field is exposed and may be shared among concurrent NetToFile
invocations. I don't know how to solve this problem without exposing this byte[]
field in the outer-scope... and I'm almost sure it may not be exposed this way.
我不想使用 lambda 或匿名方法解决方案,因为这不在并发编程"课程的课程中.
I don't want to use a lambda or anonymous method solution, because that's not in the curriculum of "Concurrent Programing" course.
推荐答案
您将需要使用来自 NetStream 读取的回调来处理此问题.坦率地说,将复制逻辑包装到它自己的类中可能更容易,以便您可以维护活动流的实例.
You are going to need to use the callback from the NetStream read to handle this. And frankly it might be easier to wrap the copying logic into its own class so that you can maintain the instance of the active Streams.
这就是我的处理方式(未测试):
This is how I'd approach it (not tested):
public class Assignment1
{
public static void NetToFile(NetworkStream net, FileStream file)
{
var copier = new AsyncStreamCopier(net, file);
copier.Start();
}
public static void NetToFile_Option2(NetworkStream net, FileStream file)
{
var completedEvent = new ManualResetEvent(false);
// copy as usual but listen for completion
var copier = new AsyncStreamCopier(net, file);
copier.Completed += (s, e) => completedEvent.Set();
copier.Start();
completedEvent.WaitOne();
}
/// <summary>
/// The Async Copier class reads the input Stream Async and writes Synchronously
/// </summary>
public class AsyncStreamCopier
{
public event EventHandler Completed;
private readonly Stream input;
private readonly Stream output;
private byte[] buffer = new byte[4096];
public AsyncStreamCopier(Stream input, Stream output)
{
this.input = input;
this.output = output;
}
public void Start()
{
GetNextChunk();
}
private void GetNextChunk()
{
input.BeginRead(buffer, 0, buffer.Length, InputReadComplete, null);
}
private void InputReadComplete(IAsyncResult ar)
{
// input read asynchronously completed
int bytesRead = input.EndRead(ar);
if (bytesRead == 0)
{
RaiseCompleted();
return;
}
// write synchronously
output.Write(buffer, 0, bytesRead);
// get next
GetNextChunk();
}
private void RaiseCompleted()
{
if (Completed != null)
{
Completed(this, EventArgs.Empty);
}
}
}
}
这篇关于.NET 异步流读/写的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!