重複するI/Oを使用するために複数のスレッドを使用するクラスを変換しようとしています。それはほとんど動作していますが、それはスレッドの問題に無作為に当たっているようです。非同期ネットワークストリーム連続読み取りデザインパターン
直接投稿するコードが多すぎますが、ここに基本パターンがあります。目標は、接続が廃棄されるまで接続からデータを読み取ってそこに座ることです。したがって、EndRead()
が完了すると、新しいBeginRead()
が開始されます。
public enum State
{
Idle,
BeforeRead,
PendingRead,
FinishingRead,
Died,
}
private int state;
private IAsyncResult asyncResult;
private byte[] readBuffer = new byte[4096];
private System.Net.Sockets.NetworkStream stream;
public void Connect(System.Net.Sockets.TcpClient client, string host, int port)
{
client.Connect(host, port);
this.stream = client.GetStream();
}
private bool SetState(State expectedState, State newState)
{
return Interlocked.CompareExchange(ref this.state, (int)newState, (int)expectedState) == expectedState;
}
public void BeginRead()
{
try
{
while (true)
{
if (!SetState(State.Idle, State.BeforeRead))
return;
IAsyncResult async;
async = stream.BeginRead(readBuffer, 0, readBuffer.Length, x => EndRead(true), null);
if (async == null)
return;
SetState(State.BeforeRead, State.PendingRead);
lock (this)
this.asyncResult = async;
if (async.AsyncWaitHandle.WaitOne(0))
EndRead(false);
}
}
catch { this.state = State.Died; }
}
private void EndRead(bool asynchronousCallback)
{
try
{
if (!SetState(State.PendingRead, State.FinishingRead))
return;
IAsyncResult async;
lock (this)
{
async = this.asyncResult;
this.asyncResult = null;
}
if (async == null)
return;
int bytesRead = stream.EndRead(async);
HandleData(bytesRead, readBuffer);
SetState(State.FinishingRead, State.Idle);
if (asynchronousCallback)
BeginRead();
}
catch { this.state = State.Died; }
}
それが動作時間のほとんどは、時折、それはいくつかのいずれかを実行します。
- 停止はI
asyncResult has already been handled: "EndReceive can only be called once for each asynchronous operation"
例外をスローします。
また、別のスレッド(stream.Write、stream.BeginWrite
ではなく)から同期書き込みが行われていることにも言及してください。私は読み書きが互いに独立しているべきだと思うので、行動に影響を与えるべきではない。
私のデザインに根本的に欠陥がありますか?これは切り捨てられた例ですので、私が取り除いたものが問題を引き起こす可能性がありますが、基本的な設計が有効かどうかを知る必要があります。非同期でチェーンを読み取る適切な方法は何ですか?
(そして場合には提案がasync/await
を使用することですそれはオプションではありませんので、このコードは、Windows XP上で実行する必要があります。)
はあなたの「ストリーム」変数のデータ型を含めてくださいでした。 – Andrew
これはSystem.Net.Sockets.NetworkStreamです(ただし、私のライブコードでは状況に応じてNegotiateStreamまたはそのどちらでもあります)。 –
私はBegin/Endパターンを長い時間前に使用していますが、 'BeginRead'からのIAsyncResultの戻り値が' AsyncCallback'で提供されたものと同じでないかもしれないというような奇妙な問題があったことを覚えています。 'AsyncCallback'(それはあなたが使用していない' x')のインスタンスで 'EndRead'を呼び出します。 さらに、 'SetState'を使った状態管理が間違っているようです。非同期コールバックは 'SetState'の前に呼び出すことができます。 また、非同期I/Oを実行してから無限ループで待機する点は何ですか?非同期コールバックからの読み取りをそのまま続行してください。 – Honza