新しいスレッドを作成して関数を実行するコードから、スレッド プールまたはタスク並列ライブラリを使用するコードに変換しようとしています。私がこれを行っているのは、ワーカー スレッドの関数が (理論的には) 無期限に実行される可能性があるにもかかわらず、各スレッドがほとんどの時間を何もせずに費やすことを知っているからです。また、接続がタイムアウトしたり、新しい接続が作成されたりする可能性があるため、ワーカー スレッドの作成と破棄のオーバーヘッドを最小限に抑える方法も必要です。それ - そして CLRProfiler が 7836 個のスレッドが 62 時間のテスト実行で/後にファイナライズされたことを示すのを見るのは少し不安で、単一の (気難しい場合) デバイスがメッセージを送信しています。
これが私がやりたいことです:
メインスレッド。
1.) TCPListener に TcpClient を受け入れさせる
2.) その TcpClient を使用するワーカー スレッドを起動する
3.) 停止するように指示されていない場合は、ステップ 1 に戻ります。
ワーカー スレッド (プール/タスクで使用するため)
1.) TcpClient からのメッセージがあるかどうかを確認します
2.) その場合、メッセージを解析し、データベースに送信し、1 秒間スリープします。
3.) それ以外の場合は、1 ミリ秒間スリープします。
4.) 停止するように指示されておらず、タイムアウトしていない場合は、手順 1 に戻ります。
元のアプローチは次のとおりです。
private AutoResetEvent connectionWaitHandle = new AutoResetEvent(false);
private static bool stop = false;
private void MainThread()
{
TcpListener universalListener = new TcpListener(IPAddress.Any, currentSettings.ListeningPort);
universalListener.Start();
while (!stop)
{
IAsyncResult result = universalListener.BeginAcceptTcpClient(WorkerThread, universalListener);
connectionWaitHandle.WaitOne();
connectionWaitHandle.Reset();
}
}
private void WorkerThread(IAsyncResult result)
{
TcpListener listener = result.AsyncState as TcpListener;
if (listener == null)
{
connectionWaitHandle.Set();
return;
}
TcpClient client = listener.EndAcceptTcpClient(result);
connectionWaitHandle.Set();
NetworkStream netStream = null;
bool timedout = false;
try
{
while (!timedout && !stop)
{
if (client.Available > 0)
{
netStream = client.GetStream();
//Get and Parse data here, no need to show this code
//The absolute fastest a message can come in is 2 seconds, so we'll sleep for one second so we aren't checking when we don't have to.
Thread.Sleep(1000);
}
else
{
//Sleep for a millisecond so we don't completely hog the computer's resources.
Thread.Sleep(1);
}
if (/*has timed out*/)
{
timedout = true;
}
}
}
catch (Exception exception)
{
//Log Exception
}
finally
{
client.Close();
}
}
私は、universalListener.BeginAcceptTcpClient(...) などを置き換えてみました。すべてと
(new Task.TaskFactory.FromAsync<TCPClient>(universalListener.BeginAcceptTcpClient, universalListener.EndAcceptTcpClient, universalListener).ContinueWith(WorkerThread);
AutoResetEvent connectionWaitHandle コードを削除するだけでなく、ワーカー スレッドが 1 回だけ起動するように見えました。
また、スレッド プールとタスク (公式ドキュメントなど) について見つけたものはすべて、それらが極端な寿命が短い。
私の質問は次のとおりです。
- スレッドプールまたはタスク並列ライブラリのタスクでさえ、長寿命のスレッドに適していますが、ほとんどがホイールスピンですか?
- もしそうなら、どうすれば正しいパターンを実装するのが最善でしょうか?
- もしそうなら、私は TaskFactory.FromAsync(...).ContinueWith(...) の使用について正しい考えを持っていましたか?