공부

[C#] RX Throttle과 ManualResetEventSlim으로 중복요청 필터링하기

복제고양이 2025. 1. 20. 21:00
300x250

 

  어쩌다 보니 CPP/CLI 에서 메인으로 프로그램을 시작해서 버튼 등의 제어를 통해 C# 프로젝트에서 중계를 해주고 

그 결과를 다시 CPP/CLI 프로젝트로 보내서 텍스트나 색으로 상태를 표현하는 프로그램을 제작하게 되었다.

 

 

문제는 메인에서 사용하는 IMGUI에서 버튼 등을 통해 딜레이 없이 짧은 시간에 한번에 많은 요청을 보낼 수 있다.

그리고 통신 관련 DLL이 그런 요청을 그대로 받아들이면 내부에서 무한 로딩에 빠지는 문제가 있다. 또한 통신 객체의 연결 끊기 및 dispose도 제대로 처리되지 못한다.

 메인 프로젝트의 imgui는 생소해서 새 기능을 구현하기 힘들고 통신 dll은 소스가 없어서 내가 수정할 수 없으니 그나마 뭔가 유연하게 고칠 수 있는 중계 프로젝트에서 처리를 해줘야 했다.

 

 

 그래서 찾은 방법은 ReactiveX 중에서 Throttle 를 사용했다. 메인 프로젝트에서 중계 프로젝트의 static 함수를 통해 

필요한 작업을 subscribe하고 통신 요청을 할때마다 onnext를 해서 

 구현한 코드에서는 사용자가 짧은 시간에 한번에 통신 요청을 보내면 0.75초동안 요청이 없는 것을 기다리다가 마지막 하나만 처리되도록 만들었다.

 이렇게 바꾸니 메인 프로젝트에 disposable 오브젝트를 전달할 수 없었다.

구조상 메인 프로젝트로 Task를 보내기는 어려워서 동기 ManualResetEventSlim을 이용했다.

 

뭔가 살짝 스파게티 코드가 된게 아닐까 싶지만 예전에 while문으로 만든, 오브젝트가 새로 바뀌는 것을 계속 기다리게 하는 코드보다는 낫다고 생각한다. 

 

 한번 메인 프로젝트에 SubscribeMonitor와 EmitMonitor를 사용해보니 원하는대로 0.75초 이후 단 하나의 통신 요청만 작동해서 문제를 해결할 수 있었다.

 private static readonly Subject<MonitorData> Source = new Subject<MonitorData>();
 private static readonly ManualResetEventSlim _resetEvent = new ManualResetEventSlim(false);
 static object disposableObj;
 
 클래스 작성..
 
  public static void SubscribeMonitor()
 {
     Source
         .Throttle(TimeSpan.FromMilliseconds(750)) // 0.75초동안 새로운 호출이 없으면 마지막 호출만 실행
         .Subscribe(m => 
         {
             disposableObj = //작업 완료 후 disposable 오브젝트 설정
             // 작업 완료 신호 설정
             _resetEvent.Set();
         });
 }

 public static object EmitMonitor(string[] data1, IEnumerable<string> data2, Action<object> data3, Action<object> data4)
 {
     var data = new MonitorData
     {
         Data1 = data1,
         Data2 = data2,
         Data3 = data3,
         Data4 = data4
     };
     // 작업 완료 신호를 초기화
     _resetEvent.Reset();
     Source.OnNext(data);
     // 신호가 설정될 때까지 대기
     _resetEvent.Wait();
     return disposableObj;
 }
 
  //데이터 전달용 MonitorData 클래스 정의
 public class MonitorData
 {
     public string[] Data1 { get; set; }
     public IEnumerable<string> Data2 { get; set; }
     public Action<object> Data3 { get; set; }
     public Action<object> Data4 { get; set; }
 }
반응형