バックグラウンド ワーカー クラスで奇妙な動作が発生しており、それがどのように機能するかを完全には理解していないと思います。以下のコード セクションは、BackgroundWorker が実装するいくつかの追加機能 (進行状況レポートなど) を除いて、ほぼ同じであると想定しました。
セクション 1:
void StartSeparateThread(){
BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerAsync();
}
void bw_DoWork(object sender, DoWorkEventArgs e)
{
//Execute some code asynchronous to the thread that owns the function
//StartSeparateThread() but synchronous to itself.
var SendCommand = "SomeCommandToSend";
var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
var SecondsToWait = 30;
//this calls a function that sends the command over the NetworkStream and waits
//for various responses.
var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
}
第2節:
void StartSeparateThread(){
Thread pollThread = new Thread(new ThreadStart(DoStuff));
pollThread.Start();
}
void DoStuff(object sender, DoWorkEventArgs e)
{
//Execute some code asynchronous to the thread that owns the function
//StartSeparateThread() but synchronous to itself.
var SendCommand = "SomeCommandToSend";
var toWaitFor = new List<string>(){"Various","Possible","Outputs to wait for"};
var SecondsToWait = 30;
//this calls a function that sends the command over the NetworkStream and waits
//for various responses.
var Result=SendAndWaitFor(SendCommand,toWaitFor,SecondsToWait);
}
セクション 1 を使用して、ネットワーク ストリーム経由で文字列を送信するコードを実行し、目的の応答文字列を待機して、その間のすべての出力をキャプチャしていました。これを行うための関数を作成しました。この関数は、ネットワークストリーム出力、送信された文字列のインデックス、および目的の応答文字列のインデックスを返します。これで奇妙な動作が見られたので、送信文字列と出力文字列の両方が見つかった場合にのみ返すように関数を変更し、見つかった文字列のインデックスが送信文字列のインデックスよりも大きいようにしました。そうしないと、永遠にループします (テストのためだけに)。関数は実際に戻りますが、両方の文字列のインデックスが -1 であり、出力文字列が null であるか、前の呼び出しの予想される出力で埋められることがあることがわかります。何が起こったのかを推測するなら、bw_DoWork() 関数内から呼び出された外部関数は、bw_DoWork() 関数を所有するスレッドに対して非同期で実行されます。その結果、 SendAndWaitFor() 関数が連続して複数回呼び出されたためです。2 番目の呼び出しは、最初の呼び出しが終了する前に実行され、最初の呼び出しの結果が返された後、評価される前に上書きされます。最初の呼び出しは常に正しく実行され、後続の呼び出しは上記の奇妙な動作を示すため、これは理にかなっているように見えますが、BackgroundWorker クラスがどのように動作するかについては直観に反しているようです。また、SendAndWaitFor 関数内で中断した場合、物事は適切に動作します。このことからも、bwDoWork 関数自体の内部で何らかのマルチスレッド化が行われていると思われます。
上記の最初のセクションのコードを 2 番目のセクションのコードに変更すると、完全に期待どおりに動作します。では、BackgroundWorker クラスを理解している人なら、何が起こっているのか説明できますか? 以下は、関連する可能性のあるいくつかの関連機能です。
ありがとう!
public Dictionary<string, string> SendAndWaitFor(string sendString, List<string> toWaitFor, int seconds)
{
var toReturn = new Dictionary<string, string>();
var data = new List<byte>();
var enc = new ASCIIEncoding();
var output = "";
var FoundString = "";
//wait for current buffer to clear
output = this.SynchronousRead();
while(!string.IsNullOrEmpty(output)){
output = SynchronousRead();
}
//output should be null at this point and the buffer should be clear.
//send the desired data
this.write(enc.GetBytes(sendString));
//look for all desired strings until timeout is reached
int sendIndex=-1;
int foundIndex = -1;
output += SynchronousRead();
for (DateTime start = DateTime.Now; DateTime.Now - start < new TimeSpan(0, 0, seconds); )
{
//wait for a short period to allow the buffer to fill with new data
Thread.Sleep(300);
//read the buffer and add it to the output
output += SynchronousRead();
foreach (var s in toWaitFor)
{
sendIndex = output.IndexOf(sendString);
foundIndex = output.LastIndexOf(s);
if (foundIndex>sendIndex)
{
toReturn["sendIndex"] = sendIndex.ToString();
toReturn["foundIndex"] = sendIndex.ToString();
toReturn["Output"] = output;
toReturn["FoundString"] = s;
return toReturn;
}
}
}
//Set this to loop infinitely while debuging to make sure the function was only
//returning above
while(true){
}
toReturn["sendIndex"]="";
toReturn["foundIndex"]="";
toReturn["Output"] =output;
toReturn["FoundString"] = "";
return toReturn;
}
public void write(byte[] toWrite)
{
var enc = new ASCIIEncoding();
var writeString = enc.GetString(toWrite);
var ns = connection.GetStream();
ns.Write(toWrite, 0, toWrite.Length);
}
public string SynchronousRead()
{
string toReturn = "";
ASCIIEncoding enc = new ASCIIEncoding();
var ns = connection.GetStream();
var sb = new StringBuilder();
while (ns.DataAvailable)
{
var buffer = new byte[4096];
var numberOfBytesRead = ns.Read(buffer, 0, buffer.Length);
sb.AppendFormat("{0}", Encoding.ASCII.GetString(buffer, 0, numberOfBytesRead));
toReturn += sb.ToString();
}
return toReturn;
}