5

SerialPort を使用して、C# で要求応答通信モジュールを実行しようとしています。これは非常に単純な実装で、動作することを示すためのものです (SerialPort が適切に動作せず (USB 仮想 COM ポート)、いくつかの文字を消費することがあります。おそらく Windows ドライバーのバグです)。

ただし、デモは機能しません:-/

オブジェクトのプロパティを読み取るフォームでプロパティ グリッドを使用し、リモート デバイスからプロパティを読み取る要求を送信すると、非常に奇妙なことが起こります。

lock{} ブロックを使用して呼び出しをシーケンシャルにしようとしましたが、うまくいきません。ロックしても、複数の通話が保護エリアに入ります。

私が間違っていることを教えてください。

私のコード:

    SerialPort sp;

    public byte[] SendCommand(byte[] command)
      {
          //System.Threading.Thread.Sleep(100);
          lock (sp)
          {
              Console.Out.WriteLine("ENTER");
              try
              {

                  string base64 = Convert.ToBase64String(command);

                  string request = String.Format("{0}{1}\r", target_UID, base64);

                  Console.Out.Write("Sending request... {0}", request);

                  sp.Write(request);

                  string response;

                  do
                  {
                      response = sp.ReadLine();
                  } while (response.Contains("QQ=="));

                  Console.Out.Write("Response is: {0}", response);

                  return Convert.FromBase64String(response.Substring(target_UID.Length));
              }

              catch (Exception e)
              {
                  Console.WriteLine("ERROR!");
                  throw e;
              }
              finally
              {
                  Console.Out.WriteLine("EXIT");
              }
          }

      }

出力:

ENTER
Sending request... C02UgAABAA=
Response is: cgAABAAARwAAAA==

EXIT
ENTER
Sending request... C02UgQARwA=
ENTER
Sending request... C02UgAABAA=
Response is: gQARwAAPHhtbD48bWVzc2FnZT5IZWxsbyBYWDIhPC9tZXNzYWdlPjxkZXN0aW5haXRvbj5NaXNpPC9kZXN0aW5hdGlvbj48L3htbD4=

間に EXIT がない 2 つの ENTER-s に注意してください。そんなことがあるものか?

4

4 に答える 4

6

lockキーワードの機能を覚えておく必要があります。これにより、1 つのスレッドのみがロックに入ることができます。問題は、スレッドを使用していないことです。このコードはすべて、プログラムのメイン スレッドである UI スレッドで実行されます。

次に知っておく必要がある詳細は、UI スレッドが特別であり、re-entrantであることです。sp.ReadLine();呼び出しは UI スレッドをブロックしようとしています。これは違法です。GUI プログラムの UI スレッドは、プログラムの Main() メソッドの [STAThread] 属性によって有効にされる「シングル スレッド アパートメント」として動作します。STA スレッドのコントラクトはブロックを禁止しているため、デッドロックが発生する可能性が非常に高くなります。

STA の要件に従うために、CLR は、UI スレッドで実行されるコードがブロッキング操作を実行するたびに、SerialPort.ReadLine() のように特別な処理を行います。メッセージ ループをポンピングして、Windows が送信するメッセージがディスパッチされ続けるようにします。このメッセージ ループは、Application.Run() と同じことを行います。

これがどこに向かっているのかがわかります。PropertyGrid は SendCommand() メソッドを再度呼び出すことができます。ロックはまったく機能しません。これは同じスレッドで発生します。

この問題を解決するのはそれほど簡単ではありません。SendMessage() がトリガーされるコードを確認できません。しかし、何らかの方法でこれが起こらないようにする必要があります。この質問のこの動作に関する詳細な背景。

于 2012-12-25T19:26:42.023 に答える
5

フィールドはどこにsp割り当てられていますか? ロックは、null 以外のオブジェクトに対してのみ機能します。

sp呼び出しごとに が別々に割り当てられている場合、ロックは相互に排他的ではありません (ロックは、同じオブジェクト インスタンスでのみ相互に排他的です)。その場合、ロックに使用する静的フィールドが必要になります。

private static readonly object _lockObject = new object();

編集: 他の回答のコメントに基づいて、実際にUIスレッドでこのロジックを実行していることがわかります。これにより、メッセージキューがポンプされると、同じスレッド(UIスレッド)でロックが複数回再入力されます。 . このコードを別のスレッドで実行すると、次の 2 つの利点が得られます。(1) この潜在的に実行時間の長いコードが実行されるときに UI がロックアップしない、(2) ロックが常に新しいスレッドで取得されるため、への後続の呼び出しSendCommandはすべて独自のスレッドで実行されるため、必要に応じて順番にロックに入ります。

于 2012-12-25T18:25:05.880 に答える
0

試す/変更する必要があることが2つあります。

1.ロックのみに使用される別のフィールドを作成します

2.ダブルロックチェックの適用:ダブルチェックロック

于 2012-12-25T18:33:07.137 に答える