[intelliJ] 멀티스레드 환경 debug

doogang·2024년 10월 29일
0

프로그램에 문제가 생겨서 어떤 값이 들어가고 있는지, 어떤 순서로 로직이 처리되고 있는지 등을 확인할 때 주로 디버깅을 활용한다.

인텔리제이에서 사진의 벌레 모양을 누르면 디버깅을 시작할 수 있다.

하나의 main 스레드 안에서만 개발과 디버깅을 수행하는 경우가 많은데, 상황에 따라 멀티 스레드 환경에서 개발이 필요할 때가 있다. 멀티 스레드 환경에서 테스트를 하면 먼저 중단점에 걸리는 하나의 thread에 대한 디버깅만 가능하고 나머지 thread에 대해서는 제대로 된 확인이 어려웠다.

그래서 멀티 스레드 환경에서는 각 스레드별 동작하는 로직을 예상하거나, 로깅이나 출력문을 통해 스레드별 결과를 확인했었는데 디버깅 중 간단한 설정을 하면 각 스레드별 디버깅이 가능하여 콜스택 등을 확인할 수 있다.

먼저 테스트용으로 아래와 같이 로직을 간단하게 만들었다. 3개의 스레드를 사용하는 ExecutorService를 만들었고, 비동기로 동작하면서 List<Integer> list에 10이 없으면 10을 넣고, 10이 있으면 지나가는 로직이다.

public class Main {
  
  private static List<Integer> list = new ArrayList<>();

  public static void main(String[] args) {
    Runnable runnable = () -> {
      String name = Thread.currentThread().getName();
      if (!list.contains(10)) {
        list.add(10);
        System.out.println(name + " thread add 10 at " + LocalDateTime.now());
      }
    };

    ExecutorService executorService = Executors.newFixedThreadPool(10);
    CompletableFuture<Void> future1 = CompletableFuture.runAsync(runnable, executorService);
    CompletableFuture<Void> future2 = CompletableFuture.runAsync(runnable, executorService);
    CompletableFuture<Void> future3 = CompletableFuture.runAsync(runnable, executorService);

    System.out.println("finish");
  }
}

이 로직을 별도 설정 없이 중단점만 설정하여 그냥 돌려보면 지정한 중단점을 지나는 모든 스레드별 디버깅이 어렵고, 인텔리제이에서 아래와 같이 중단점을 skip 했다는 메서지를 보여준다.

위의 Troubleshooting guide를 타고 들어가면 아래와 같은 중단점 스킵 트러블슈팅 페이지로 이동하게 된다.

위 Troubleshoot 페이지에서 only pause the current thread를 누르면 인텔리제이 suspend policy 설명 페이지가 나오는데, 이걸 읽어보면 중단점은 중단 정책(policy)를 설정할 수 있는데 AllThread로 나뉜다.

policy 설정에 대한 설명을 간단히 해보자면

  • All : 여러 스레드 중 어떤 스레드가 중단점에 도착하면 모든 스레드가 중단되며, 도착한 하나의 스레드에 대해서만 디버깅이 가능하다. 다른 스레드들의 디버깅 과정은 skip 되며, 도착한 하나의 스레드의 디버깅이 끝나면 전체 디버깅이 종료된다.
  • Thread : 지정한 중단점을 지나는 모든 스레드들이 해당 중단점에서 중단된다. 중단점을 지나는 모든 스레드들이 지정한 중단점에서 멈춰있기 때문에 지정한 중단점에서의 각 스레드별 디버깅이 가능하다. 아래 사진처럼 중단점에서의 각 스레드를 선택하여 변경하면서 디버깅할 수 있다. (thread1, thread2, thread3 모두 확인 가능)

그렇다면 중단점 suspend policy 설정을 해야하는데 방법이 아주 간단하다. 하던대로 원하는 중단점을 지정해두고 중단점을 우클릭해보면 아래와 같이 중단점에 대한 설정이 나올 것이다.

여기서 suspend 설정을 Thread 로 변경해주면 된다.

참고로 default가 All로 되어있어서 혹시 Thread 로 default 설정을 하고 싶으면 오른쪽에 Make Default를 선택하면 된다.

이제부터 각 스레드별 디버깅을 해볼건데, 아래처럼 list에 10이 포함되었는지 확인하는 if문과 list에 10을 add하는 로직 2곳에 중단점을 걸고 Thread policy로 설정하여 테스트를 해봤다.

총 3개의 스레드를 사용했는데 이를 각각 thread1, thread2, thread3라고 이름을 붙였을 때, 첫번째 중단점(if문)에서 당연히 thread1~3 모두 list에 값이 없는 상태이다.

우선 thread1을 선택하여 먼저 디버깅을 해서 if문을 지나 if문 안에 있는 두번째 중단점(list.add(10))을 수행하여 list에 10을 추가하였다.

다음으로 thread2를 선택하여 디버깅할 스레드를 바꿔주었다.

thread2는 아직 아무것도 안했으니 첫번째 중단점에 머물러 있고,

첫번째 중단점에서 thread2안의 값들을 보면 list에 thread1에서 채운 10이 있는 것을 볼 수 있다.

이미 list에 10이 채워져있었기에 디버깅을 계속 진행했을 때, if문 안으로 들어가지 못해 두번째 중단점으로 이동하지 않고 바로 종료되었다.

이번엔 thread3를 디버깅했는데, 결과는 thread2와 동일하게 thread1에서 list에 추가한 10으로 인해 if문을 수행하지 못하고 바로 종료되었다.

다시 thread1으로 돌아와 if문 안의 출력문을 수행하고

console창을 확인해보면 thread1에서 수행한 출력문이 찍혀있는 것을 확인할 수 있다.

이렇게 멀티스레드 환경에서 디버깅을 진행해봤는데, 개인적으로 굉장히 재밌었다.

지정한 중단점에서의 각 스레드별 상태나 콜스택을 정확히 볼 수 있어서 좋았고, 각 스레드를 쉽게 이동하면서 제어하기 어려운 멀티 스레드 환경을 제어할 수 있었던 것도 재밌었다. ex) 위에서 한 것처럼 thread1을 먼저 선택하고 조작하여 list에 10을 thread1에서 넣을 수 있도록 컨트롤할 수 있었던 것

멀티 스레드 환경에서 각 스레드별 디버깅이 필요할 때 사용해보면 좋을 것 같다.

profile
안녕하세요

1개의 댓글

comment-user-thumbnail
2024년 11월 18일

비밀댓글 입니다.

답글 달기