非同期プログラミング構造におけるデバッグの問題の解決|


前の記事では、非同期プログラミング構造のデバッグ問題について説明しました.今日は、問題の原因とコードの修正方法についてまとめます.
非同期プログラミング構造におけるデバッグの問題

問題コード


以下は私が作成したモジュールに2つのバグがあるPseudoeコードです.2つのセグメントコードは、異なる実行フロー(スレッド)を有し、相互作用する.送信側は、通信チャネルにコマンドを送信し、応答を待機し、受信側は応答を受信し、送信側に応答を受信するように通知する.

送信部

loop {
	Message command = commandQueue.pop(); // 1

	channel.write(command); // 2

	SyncObject syncObj = new SyncObject(); // 3
	syncMap.put(command.id, syncObj); // 4

	if(!syncObj.wait(2000)) { // 5
		throw new Exception("no response"); // 6
	}
}

じゅしんたんし

loop {
	Message response= responseQueue.pop(); // 1
	syncObj = syncMap.get(response.id); // 2

	if (syncObj != null) { // 3 
		syncObj.signal();  // 4 
		syncMap.remove(response.id); // 5
	}

	pipeline.next(response); // 6
}

もんだいげんしょう


上記のコードを実行すると、間欠的な「no response」異常が発生します.受信側にログが残ると、応答が明確に受信されます.特に、受信側コードで受信イベントアラートを発行するために使用される同期オブジェクトは、問題が発生したときに空である.解析の結果、同期オブジェクトの間欠が空の理由は2つあります.
	if (syncObj != null) { ... } 

予期せぬ実行フロー


送信側と受信側のコードの予想される流れは以下の通りである.私の頭の中でよく機能しています.しかし,2つの実行フローがどのように切り替えて実行されたかを行ごとに記録した結果,全く予想外の状況が発生したことが分かった.
  • 次のコマンド同期オブジェクト
  • を削除する.
  • 同期オブジェクト追加前応答

  • 1.次のコマンド同期オブジェクトの削除


    問題が発生した場合、コードの実行順序は次のとおりです.したがって、受信側が前のコマンドの同期オブジェクトを削除すると、次のコマンドの同期オブジェクトが予期せぬ削除され、実際に次の応答が受信されると、同期オブジェクトが空になります.
  • 受信側4号線が動作し、応答完了イベントが送信側に送信される.
  • エミッタ5号線(待機応答)はWaiting状態から離れます.
  • 受信側は運転を停止し、送信側は運転を継続する.(Context Switch)
  • エミッタはループを迂回し、キューから次のコマンドを取り出し、次のコマンドの応答を同期させmapを挿入するためにオブジェクトの作成を完了します.(~エミッタ4号線)
  • は、受信側を再実行します.(Context Switch)
  • 受信側5号線は、Mapから同期オブジェクトを削除する.
  • このとき、次のコマンド応答を同期するために挿入された同期オブジェクトが削除されます.
  • 2.同期オブジェクトを挿入する前に応答を受信する


    また、問題が発生した場合、コードは以下のより巧みな順序で実行される可能性があります.このため、送信側がコマンドを発行した後に同期オブジェクトを作成し、それをMapに挿入する前に応答を受信し、受信側の同期オブジェクトが空になる.
  • 送信側2号線が動作し、コマンドがチャネルを介して伝送される.
  • 送信側3号線が動作し、応答同期オブジェクトが生成される.
  • 送信側4号線が運転される前に、受信側1号線はチャネルから応答を受信し、受信側1号線は起動状態で起動する.
  • 送信側は運転を停止し、受信側は運転を継続する.(Context Switch)
  • 受信側2号線が運転された後、同期オブジェクトはまだMapに挿入されておらず、空です.
  • 解決する


    コードの2つの部分を修正することで、問題を解決することができます.
  • は、同期オブジェクトを作成された送信側から削除することに応答し、非同期時間の問題を解消する.
  • コマンドを送信する前に、事前同期オブジェクトを作成してMapに追加すると、応答がすぐに受信された場合でも、同期オブジェクトが空であるという問題が解消されます.

  • 送信部

    loop {
    	Message command = commandQueue.pop();
    
    	SyncObject syncObj = new SyncObject(); // 명령 전송 전 동기화 객체 생성과 Map 삽입
    	syncMap.put(command.id, syncObj); 
    
    	channel.write(command);
    
    	if(!syncObj.wait(2000)) {
    		throw new Exception("no response"); 
    	}
    
    	syncMap.remove(response.id); // 동기화 객체 제거
    }

    じゅしんたんし

    loop {
    	Message response= responseQueue.pop(); // 1
    	syncObj = syncMap.get(response.id); // 2
    
    	if (syncObj != null) { // 3 
    		syncObj.signal();  // 4 
    	}
    
    	pipeline.next(response); // 6
    }

    に感銘を与える


    2つの非同期実行ストリームで発生した問題は私の頭の中であまりよく描かれていません.私はコードを1行1行デバッグして、自分の目で見る前に、それを全く予想していなかったようです.もっと経験があれば、これらのケースはコードだけで見ることができますか?いかがでしたか?作成中は、同期実行のコードを記述するのが難しい場合があります.