15

.NET2.0 SerialPortの.BaseStreamプロパティを使用して、非同期の読み取りと書き込み(BeginWrite / EndWrite、BeginRead / EndRead)を実行しようとしています。

私はこれである程度の成功を収めていますが、しばらくすると、(Process Explorerを使用して)アプリが使用しているハンドルが非常に徐々に増加し、場合によっては余分なスレッドが増加し、ハンドル数も増加することに気付きます。

コンテキストスイッチレートも、新しいスレッドが表示されるたびに増加します。

アプリは常に3バイトをPLCデバイスに送信し、その代わりに800バイト程度を取得し、57600のボーレートで送信します。

初期のCSwitchデルタ(これもProcess Explorerから)は約2500で、とにかく非常に高いようです。新しいスレッドが表示されるたびに、この値が増加し、それに応じてCPU負荷が増加します。

誰かが似たようなことをしてくれて、私を助けてくれることを願っています。あるいは、「神の名において、そのようにしないでください」とさえ言ってくれることを願っています。

以下のコードでは、「this._stream」はSerialPort.BaseStreamから取得され、CommsResponseはIAsyncresult状態オブジェクトとして使用するクラスです。

このコードは、シリアルポートを使用する代わりに作成するTCP接続に共通です(シリアルポートとTCPチャネルが派生したCommsChannel基本クラスがあります)。これらの問題はないので、かなり期待しています。 CommsResponseクラスには何の問題もありません。

ありがたいコメントをいただきました。

    /// <summary>
    /// Write byte data to the channel.
    /// </summary>
    /// <param name="bytes">The byte array to write.</param>
    private void Write(byte[] bytes)
    {
        try
        {
            // Write the data to the port asynchronously.
            this._stream.BeginWrite(bytes, 0, bytes.Length, new AsyncCallback(this.WriteCallback), null);
        }
        catch (IOException ex)
        {
            // Do stuff.
        }
        catch (ObjectDisposedException ex)
        {
            // Do stuff.
        }
    }

    /// <summary>
    /// Asynchronous write callback operation.
    /// </summary>
    private void WriteCallback(IAsyncResult ar)
    {
        bool writeSuccess = false;

        try
        {
            this._stream.EndWrite(ar);
            writeSuccess = true;
        }
        catch (IOException ex)
        {
            // Do stuff.
        }

        // If the write operation completed sucessfully, start the read process.
        if (writeSuccess) { this.Read(); }
    }

    /// <summary>
    /// Read byte data from the channel.
    /// </summary>
    private void Read()
    {
        try
        {
            // Create new comms response state object.
            CommsResponse response = new CommsResponse();

            // Begin the asynchronous read process to get response.
            this._stream.BeginRead(this._readBuffer, 0, this._readBuffer.Length, new AsyncCallback(this.ReadCallback), response);
        }
        catch (IOException ex)
        {
            // Do stuff.
        }
        catch (ObjectDisposedException ex)
        {
            // Do stuff.
        }
    }

    /// <summary>
    /// Asynchronous read callback operation.
    /// </summary>
    private void ReadCallback(IAsyncResult ar)
    {
        // Retrieve the comms response object.
        CommsResponse response = (CommsResponse)ar.AsyncState;

        try
        {
            // Call EndRead to complete call made by BeginRead.
            // At this point, new data will be in this._readbuffer.
            int numBytesRead = this._stream.EndRead(ar);

            if (numBytesRead > 0)
            {
                // Create byte array to hold newly received bytes.
                byte[] rcvdBytes = new byte[numBytesRead];

                // Copy received bytes from read buffer to temp byte array
                Buffer.BlockCopy(this._readBuffer, 0, rcvdBytes, 0, numBytesRead);

                // Append received bytes to the response data byte list.
                response.AppendBytes(rcvdBytes);

                // Check received bytes for a correct response.
                CheckResult result = response.CheckBytes();

                switch (result)
                {
                    case CheckResult.Incomplete: // Correct response not yet received.
                        if (!this._cancelComm)
                        {
                            this._stream.BeginRead(this._readBuffer, 0, this._readBuffer.Length,
                                new AsyncCallback(this.ReadCallback), response);
                        }
                        break;

                    case CheckResult.Correct:  // Raise event if complete response received.
                        this.OnCommResponseEvent(response);
                        break;

                    case CheckResult.Invalid: // Incorrect response
                        // Do stuff.
                        break;

                    default: // Unknown response
                        // Do stuff.
                        break;
                }
            }
            else
            {
                // Do stuff.
            }
        }
        catch (IOException ex)
        {
            // Do stuff.
        }
        catch (ObjectDisposedException ex)
        {
            // Do stuff.
        }
    }
4

4 に答える 4

5

いくつかの提案:

3 バイトしか送信していないため、同期書き込み操作を行うことができます。遅延はあまり問題になりません。

また、常に新しい AsyncCallback を作成しないでください。1 つの Read と 1 つの Write AsyncCallback を作成し、すべての begin 呼び出しでそれを使用します。

于 2009-01-23T13:27:05.337 に答える
4

BeginWrite はまったく必要ありません。送信するのは 3 バイトだけで、送信バッファーに簡単に収まり、次のセットを送信するときにバッファーが空であることを常に確認できます。

シリアル ポートは TCP/IP 接続よりもはるかに遅いことに注意してください。受信した 1 バイトごとに BeginRead() を呼び出すことになる可能性が高いです。これにより、スレッド プールが適切に機能し、多くのコンテキスト スイッチが発生することは間違いありません。ハンドルの消費についてはよくわかりません。デバッガーを接続せずに必ずテストしてください。

BeginRead() の代わりに DataReceived を試すことは、ぜひ試してみてください。プッシュではなくプル。何かが起こったときに、スレッドプール スレッドを常にアクティブにするのではなく、スレッドプール スレッドを使用します。

于 2009-01-24T04:02:00.533 に答える
0

デバイスからの応答は常に固定サイズですか?SerialPort.Readその場合は、パケットサイズを使用して渡します。これはブロックされるので、と組み合わせDataReceivedます。さらに良いことに、応答が常に同じ文字で終了し、この終了署名がパケット内で一意であることが保証されている場合は、NewLineプロパティを設定してを使用しますReadLine。これにより、将来のパケットサイズの変更から保護されます。

于 2011-05-30T20:36:47.023 に答える
0

シリアル ポートから受信したデータを直接ファイルに送信することはできますか? 高いボー レート (1 メガボー) では、この量のノンストップ データを処理することは困難です。

于 2009-03-26T20:13:30.963 に答える