私はLineWriterとAsyncLineWriterという2つのクラスを持っています。私の目標は、呼び出し元がAsyncLineWriterのメソッドで複数の保留中の非同期呼び出しをキューに入れることができるようにすることですが、カバーの下では本当にそれらを並行して実行することはできません。すなわち、何らかの形でキューに入れてお互いを待たなければならない。それでおしまい。この質問の残りの部分は完全な例を示していますので、私が本当に求めていることについて絶対的にあいまいさはありません。C#非同期操作を強制的に同期させる方法
LineWriterを実行するのに約5秒かかる単一の同期方法(WriteLineメソッド)を有する:
public class LineWriter
{
public void WriteLine(string line)
{
Console.WriteLine("1:" + line);
Thread.Sleep(1000);
Console.WriteLine("2:" + line);
Thread.Sleep(1000);
Console.WriteLine("3:" + line);
Thread.Sleep(1000);
Console.WriteLine("4:" + line);
Thread.Sleep(1000);
Console.WriteLine("5:" + line);
Thread.Sleep(1000);
}
}
AsyncLineWriterだけLineWriterをカプセル化し、非同期インターフェイス(BeginWriteLineとEndWriteLine)を提供する:
public class AsyncLineWriter
{
public AsyncLineWriter()
{
// Async Stuff
m_LineWriter = new LineWriter();
DoWriteLine = new WriteLineDelegate(m_LineWriter.WriteLine);
// Locking Stuff
m_Lock = new object();
}
#region Async Stuff
private LineWriter m_LineWriter;
private delegate void WriteLineDelegate(string line);
private WriteLineDelegate DoWriteLine;
public IAsyncResult BeginWriteLine(string line, AsyncCallback callback, object state)
{
EnterLock();
return DoWriteLine.BeginInvoke(line, callback, state);
}
public void EndWriteLine(IAsyncResult result)
{
DoWriteLine.EndInvoke(result);
ExitLock();
}
#endregion
#region Locking Stuff
private object m_Lock;
private void EnterLock()
{
Monitor.Enter(m_Lock);
Console.WriteLine("----EnterLock----");
}
private void ExitLock()
{
Console.WriteLine("----ExitLock----");
Monitor.Exit(m_Lock);
}
#endregion
}
私が最初の段落で述べたように、私の目標は一度に1つの保留中の非同期操作だけを許可することです。私は本当にのように私はここでロックを使用する必要はなかった場合、つまり、BeginWriteLineがIAsyncResultハンドルを返すことができ、常に即座に返され、期待される動作を保持していれば、それは素晴らしいでしょうが、それを行う方法を理解できません。だから私が何をしているのかを説明する次善の方法は、ロックを使うことだけだった。
まだ、私の「ロックスタッフ」セクションが期待どおりに機能していません。私は上記のコード(非同期操作が始まる前にEnterLockを実行し、非同期操作が終了した後にはExitLockを実行する)が、一度に1つの保留中の非同期操作を実行できることを期待しています。私は次のコードを実行した場合
:
static void Main(string[] args)
{
AsyncLineWriter writer = new AsyncLineWriter();
var aresult = writer.BeginWriteLine("atest", null, null);
var bresult = writer.BeginWriteLine("btest", null, null);
var cresult = writer.BeginWriteLine("ctest", null, null);
writer.EndWriteLine(aresult);
writer.EndWriteLine(bresult);
writer.EndWriteLine(cresult);
Console.WriteLine("----Done----");
Console.ReadLine();
}
を私は次の出力を参照するを期待:
----EnterLock----
1:atest
2:atest
3:atest
4:atest
5:atest
----ExitLock----
----EnterLock----
1:btest
2:btest
3:btest
4:btest
5:btest
----ExitLock----
----EnterLock----
1:ctest
2:ctest
3:ctest
4:ctest
5:ctest
----ExitLock----
----Done----
をしかし、その代わりに、私は、彼らがすべてだ意味し、以下を参照してくださいあたかもロックが効果を持たないかのように並行して実行されます:
----EnterLock----
----EnterLock----
----EnterLock----
1:atest
1:btest
1:ctest
2:atest
2:btest
2:ctest
3:atest
3:btest
3:ctest
4:atest
4:btest
4:ctest
5:atest
5:btest
5:ctest
----ExitLock----
----ExitLock----
----ExitLock----
----Done----
私はロックが「無視されている」と仮定しています。なぜなら、私が間違っていれば私を修正するからです。私の質問は:どのようには私の期待どおりの動作を得ることができますか?そして、 "非同期操作を使わないでください"というのは受け入れられない答えです。
非同期操作を使用すると、実際には複数の真の並列操作を行うことができない場合がありますが、実際にはより複雑で実用的な使用例がありますが、少なくともキュー操作をキューに入れていても動作をエミュレートする必要があります。それらを次々と実行しています。具体的には、AsyncLineWriterへのインタフェースは変更するべきではありませんが、内部的な動作はスレッドセーフな方法で与えられた非同期操作をキューに入れておく必要があります。私の実際のケースでは、これは私が変更できないメソッドなので、私はLineWriterのWriteLineにロックを追加できないという別の問題があります(この例では実際に期待される出力が得られます)。
同様の問題を解決するために設計されたいくつかのコードへのリンクは、適切な経路で私を得るのに十分かもしれません。あるいは、いくつかの代替案。ありがとう。
P.S.どのようなユースケースがこのようなものを使用するのか疑問に思っているなら、一度に1つの操作しかアクティブにできないネットワーク接続を維持するクラスです。私は各操作のために非同期呼び出しを使用しています。本当に並行した通信回線を単一のネットワーク接続で運用するための明白な方法はないため、相互に待ち合わせる必要があります。
私が「期待した」と言ったところでは、私はおそらく「望んでいる」と言っていたはずです。 –