質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Q&A

解決済

2回答

4880閲覧

Queue が空になる現象の原因分析方法と対策について

GuielNo4

総合スコア88

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

0グッド

0クリップ

投稿2017/07/20 12:45

編集2017/07/21 02:32

###前提・実現したいこと
Queue にデータがあったのに、ゼロになってしまう現象にあった方がいらっしゃいましたら、
原因分析のアドバイスを頂ければと考えております。

###発生している問題・エラーメッセージ
Queue が空なのに Peek しているため、
「InvalidOprationException」が発生してしまいました。

###該当のソースコード

Program.cs class Program { volatile static bool _keyReaded = false; static void Main(string[] args) { ConvertFrame convertFrame = new ConvertFrame(); bool isFinish = false; while (!isFinish) { convertFrame.update(); Console.WriteLine(convertFrame.listFrames.Count.ToString()); /// キー入力で終了します。 if (_keyReaded) { isFinish = true; } } } static void MonitoringKey(object userState) { ConsoleKeyInfo keyInfo = Console.ReadKey(true); _keyReaded = true; } } ConvertFrame.cs class ConvertFrame { private Queue<byte> receivedDatas = new Queue<byte>(); private int backup_count = 0; public List<byte[]> listFrames { get; private set; } private Serial uart = new Serial(); public ConvertFrame() { listFrames = new List<byte[]>(); uart.Open("COM1", 115200); } public void update() { Queue<byte> pickedup = new Queue<byte>(); uart.getData(ref pickedup); if (pickedup.Count.Equals(0)) { return; } foreach( int index in Enumerable.Range(0, pickedup.Count ) ) { receivedDatas.Enqueue( pickedup.Dequeue() ); } if ( receivedDatas.Count.Equals(0) ) { return; } backup_count = receivedDatas.Count; // --- (1) byte head = receivedDatas.Peek(); //---- (2) head = 16; /// ---- (A) bool isExistDatas = true; while( isExistDatas ) { if ( receivedDatas.Count >= (int)head ) { List<byte> frame = new List<byte>(); foreach (int index in Enumerable.Range(0, (int)head )) { frame.Add(receivedDatas.Dequeue()); // ---(3) } listFrames.Add( frame.ToArray() ); } else { isExistDatas = false; } } } } Serial.cs class Serial { private SerialPort targetPort; private List<byte[]> receivedBytes = new List<byte[]>(); private byte[] pickedupBytes = new byte[4096]; public Serial() { } public void Open(string namePort, int baudRate) { string PortName = namePort; int BaudRate = baudRate; Parity Parity = Parity.None; int DataBits = 8; StopBits StopBits = StopBits.One; targetPort = new SerialPort(PortName, BaudRate, Parity, DataBits, StopBits); targetPort.Handshake = System.IO.Ports.Handshake.None; targetPort.ReadTimeout = 0; targetPort.WriteTimeout = 1500; targetPort.ReadBufferSize = 1024 * 12; targetPort.WriteBufferSize = 1024 * 12; targetPort.DataReceived += new SerialDataReceivedEventHandler(ReceivedHandler); targetPort.Open(); } public void Close() { targetPort.Close(); } public bool getData(ref Queue<byte> pickedup) { lock (receivedBytes) { if (receivedBytes.Count.Equals(0)) { return false; } foreach (byte[] array in receivedBytes) { foreach (byte data in array) { pickedup.Enqueue(data); } } /// 引数に渡したのでクリアする。 receivedBytes.Clear(); } return true; } private void ReceivedHandler(object sender, SerialDataReceivedEventArgs e) { int totalCount = 0; try { totalCount = targetPort.Read(pickedupBytes, totalCount, pickedupBytes.Length); } catch (TimeoutException exception) { Console.WriteLine(exception); Console.ReadKey(); } /// 固定長サイズのピックアップバッファから、受信データ数の分だけ取り出しておく。 byte[] buffer = new byte[totalCount]; Array.Copy(pickedupBytes, 0, buffer, 0, totalCount); lock (receivedBytes) { receivedBytes.Add(buffer); } return; } }

(2) で「InvalidOprationException」が発生し、ウォッチで確認すると、
確かに、receivedDatas.Count が ゼロ になっており、Peekでエラーなっていることは理解できるのですが、
直前の(1) backup_count は 0x20 とゼロではないため、
今後、どのような分析を行うべきか、質問させて頂きました。

追記:
問題の発生する部分だけ抽出してコード全体を書き直しました。
上記のコードを実際に走らせ、現象の再発を確認できしました。
ただ、今度は(3) で発生するようになりました。
このとき、backup_count = 0x36 , head = 0x06 なのですが、
例外で Queue が空という内容でした。

また、(A)の部分ですが、head の値が大きい場合に問題が発生している可能性も考えて、
16 byte に変更しています。だた、同じ例外が発生しました。

アドバイスの程、宜しくお願い申し上げます。

###補足情報(言語/FW/ツール等のバージョンなど)
visual studio 2013
.NET Framework 3.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

yuki23

2017/07/20 12:53

receivedDatas の使用箇所が書かれているようですが、肝心の receivedBuffer の使用箇所がわからないと回答のしようがありません。
GuielNo4

2017/07/20 12:59

ご指摘ありがとうございます。receivedBuffer は receivedDatas の間違いでした。修正致しました。宜しくお願い致します。
yuki23

2017/07/20 13:17

修正ありがとうございます。追加で質問があります。(1) receivedDatas を操作しているメソッドは update() だけですか? (2) 1 が正しい場合、 update() はどこから呼ばれますか? (3) このクラスのインスタンスはどこがどのように管理していますか?
momon-ga

2017/07/20 13:23 編集

誤記があったのは、ソースを抜粋してるから?それともコピーするものを間違えたから?見た感じ問題ないと思いますが。どこかでclearとかしてません?マルチスレッドでさわってるとか?
GuielNo4

2017/07/20 13:53

yuki23 さん momon-ga さん、質問ありがとうございます。メソッドの多いクラスのため行数が多く、抜粋して投稿させて頂きました。誤記があり申訳ありません。
GuielNo4

2017/07/20 13:58 編集

少々時間がかけてでも、(1)(2)(3)の要望に応えられるよう、質問のソースコードに追記させて頂きます。
GuielNo4

2017/07/21 02:33

実際に走らせてみたコードを掲載いたしました。宜しくお願い致します。
yuki23

2017/07/21 13:50

追記ありがとうございます。すみませんが、追加で質問です。(4) 起動してからどの程度で発生しますか? そのタイミングはいつも同じですか?
GuielNo4

2017/07/22 02:57

質問有難うございます。発生タイミングですが、起動から30秒で発生することもあれば、2分後に発生することもあります。
GuielNo4

2017/07/22 03:01

シリアル通信の受信データ総数か、単位時間当たりの受信数が発生タイミングに影響していないか、確認してみたいと思います。
guest

回答2

0

ベストアンサー

現時点で原因は私にもわかっていないのですが、原因分析のアドバイスをとのことですので、思い当たることを回答欄に書いておきます。

  • receivedDatas で例外が発生することは、問題の原因ではなく結果ではないか? 他のところで問題が発生した結果、たまたま receivedDatas で例外が発生しているのではないか
  • Serial クラスの receivedBytes の排他が上手く行っていないのではないか。lock(receivedBytes) ではブロックとして排他が取れるわけではない
  • (A) を入れなかった場合、 head が 0 になると receivedDatas が無限に増え続けるのではないか(Dequeue されないため)

投稿2017/07/21 13:49

yuki23

総合スコア1448

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GuielNo4

2017/07/22 03:09

分析方法や推論の進め方に関しまして、アドバイスありがとうございます。 構文ミスや引数の数値ミス、メソッドの仕様理解ミスを探すというより、 もう少々視野を広くして、コード全体のデザインや、例外の仕組みを対象とした原因分析を進めようと思います。 ありがとうございました。
guest

0

素人発言かもですが・・・

receivedDatas --- (1)
receivedBuffer --- (2)

は、同じインスタンス(オブジェクト)なのでしょうか?

追記

foreach (int index in Enumerable.Range(0, (int)head ))

headが固定なので、要素数に関係なくループをぶんまわすので、要素数が16(headの値)
未満になると例外でてるのではないでしょうか。

投稿2017/07/20 12:54

編集2017/07/21 05:10
momon-ga

総合スコア4820

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

GuielNo4

2017/07/20 12:58

ご指摘ありがとうございます。 修正しました。宜しくお願い致します。
GuielNo4

2017/07/21 05:58

>要素数に関係なくループをぶんまわすので、要素数が16(headの値) その前段で、 if ( receivedDatas.Count >= (int)head ) で 16 以下のときはループに行かないようにコーディングしているつもりなのですが、どうでしょうか?
momon-ga

2017/07/23 02:24 編集

countが16だと、0~16の17回まわりません?
GuielNo4

2017/07/23 03:14

!! 確かに。 ゼロになることばかりに気を取られていました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問