Inflearn brand logo image

인프런 커뮤니티 질문&답변

just kim님의 프로필 이미지
just kim

작성한 질문수

C# 프로그래밍 기초부터 실전 활용까지

스레드(임계영역 - Mutex)

thread join 사용

해결된 질문

작성

·

66

0

안녕하세요. .thread 연습 하다가 물어 볼꺼 있어서요...

 

Winform에 쓰레드를 생성했습니다.

thread 파일을 초기엔 null로 했습니다.

그리고 작업을 진행하다가 아래 StartThread를 호출 합니다.

private void StartThread()

{

if (thread == null)

{

thread = new Thread(new ThreadStart(GoThread);

GoThread.IsBackground = true;

GoThread.Priority = GoThread.Normal;

GoThread.Start();

}

if (!GoThread.IsAlive)

{

GoThread.Start();

}

GoThread.Join(); // 호출 함수가 끝날때까지 대기 해보자. -- 여기 질문

thread = null; // null을 넣는건 좀 부담 스러움

}

 

// 쓰레드 함수 진행

void GoThread()

{

try

{

while (queue.Count != 0)

{

// 큐에 쌓여 있는 작업 진행

queue.Dequeue();

// 쓰레드 안에 있으니 아래 처럼 인보크 사용해서 지행

this.Invoke((MethodInvoker)delegate

{

textBoxOutputLog.Text = ResultLog;

});

Thread.Sleep(1);

}

}

// 작업 완료 아래 처럼 null을 쓰고 싶은데 null을 넣기에는 부담 그래서 호출한 함수에서 join으로 대기해보자.

//thread= null;

}

질문 입니다.

GoThread.Join() 을 하니깐 쓰레드 함수가 다 종료가 될때까지 formview가 멈춥니다.

흠.... 원래 join이 스레드가 종료될때까지 기다린다는건 알겠는데 UI 진행도 멈추는건지 궁금 하네요...

그리고 쓰레드에 null을 넣는건 좋은 방법 인가요?

 

아 그리고 이건 부가적인 질문인데 ..

클레스가 생성하면 자동으로 종료되는건 알고 있습니다. 프로그램에서 직접 종료하는 방법 없을까요?? 이유을 설명하자면

클래스 생성 . 작업 진행 .. 진행중 쓰레드 처리 ... 쓰레드 처리 끝... (할일을 다함).. 종료... (이게 너무 오래 걸림)

C++에는 delete 가 있는데 ..

 

어째거나 이렇습니다.

 

 

답변 4

0

Kaburi님의 프로필 이미지
Kaburi
지식공유자

엄청난 열정에 놀랐습니다. ㅎㅎ 아래에 AI 답변도 존재하지만 추가로 정리해드리겠습니다.

 

WinForms에서 Join() 사용 시 UI 멈춤현상

  • 메인(UI) 쓰레드에서 GoThread.Join()을 호출하면, 해당 쓰레드가 종료할 때까지 현재 쓰레드에서 대기하게 됩니다.

  • UI와 관련된 처리는 모두 메인 스레드에서 이뤄지므로 Join()에서 대기하는 동안 폼이 멈춘것 처럼 보이게 돼요.

**해결방안**

  • 쓰레드 작업이 끝나는 것을 기다리면서 UI가 멈추지 않게 하려면,
    Join()을 직접 호출하지 말고, 쓰레드 작업이 끝났을 때 UI에 알리거나(예: 이벤트, 콜백),
    BackgroundWorker, Task, async/await 등 비동기 프로그래밍 패턴을 사용하는 것이 좋습니다.
    (일반적으로 async/await를 가장 추천 드립니다.)


쓰래드 객체를 null로 설정하는 것?

  • 쓰레드가 끝나면, 해당 Thread 객체는 더 이상 사용할 수 없으므로 필요하면 null로 만들어주는 것도 괜찮은 방법입니다. (null 을 할당하여 참조를 해제해주면 가비지 컬렉터가 빠르게 수거할 수 있어요.)

  • 하지만 꼭 null로 만들 필요는 없고 쓰래드가 살아있는지(IsAlive 속성)만 체크해서 필요할 때 Thread객체를 새로 만들어도 괜찮습니다.


C#에서 클래스의 명시적 종료(소멸) 방법

  • C#에는 C++의 delete처럼 객체를 “즉시” 소멸시키는 문법이 없습니다.

  • C#의 객체는 해당 객체를 참조하는 곳이 없을 때(즉, 사용하지 않게 되었을 때)
    가비지 컬렉터(GC)가 자동으로 메모리를 정리(소멸)합니다.

  • IDisposable 인터페이스를 구현하고 Dispose()를 호출하는 것도 사실 메모리 자체는 GC가 회수하는 것 입니다.

0

just kim님의 프로필 이미지
just kim
질문자

위 질문에 이렇게 바꿔 봤어요 이게 더 좋을꺼 같긴한데 고칠 부분이 있을까요?

private void StartThread()

{

if (thread == null || !GoThread.IsAlive)

{

thread = new Thread(new ThreadStart(GoThread);

GoThread.IsBackground = true;

GoThread.Priority = GoThread.Normal;

GoThread.Start();

}

}

 

// 쓰레드 함수 진행

void GoThread()

{

try

{

while (queue.Count != 0)

{

// 큐에 쌓여 있는 작업 진행

queue.Dequeue();

// 쓰레드 안에 있으니 아래 처럼 인보크 사용해서 지행

this.Invoke((MethodInvoker)delegate

{

textBoxOutputLog.Text = ResultLog;

});

Thread.Sleep(1);

}

}

}

Kaburi님의 프로필 이미지
Kaburi
지식공유자

제시해주신 코드를 개선해보자면 아래와 같습니다.

private void StartThread()
{
    if (thread == null || !thread.IsAlive)
    {
        thread = new Thread(GoThread);
        thread.IsBackground = true;
        thread.Priority = ThreadPriority.Normal;
        thread.Start();
    }
}

// 쓰레드 함수 예시 (queue를 thread-safe하게 사용)
void GoThread()
{
    try
    {
        while (true)
        {
            object item = null;
            lock (queue)
            {
                if (queue.Count == 0) break;
                item = queue.Dequeue();
            }

            // UI 갱신
            this.Invoke((MethodInvoker)delegate
            {
                textBoxOutputLog.Text = ResultLog;
            });

            // 필요에 따라 대기
            Thread.Sleep(1);
        }
    }
}

queue는 thread에 안전하지 않으므로 경합상태가 발생할 수 있습니다.

따라서 lock으로 감싸주는 것이 안전합니다.

 

또는 thread-safe한 System.Collections.Concurrent.ConcurrentQueue<T> 를 활용하는 방법도 있습니다. ConcurrentQueue 를 사용하면 lock으로 감싸주지 않아도 thread에 안전합니다.

ConcurrentQueue<string> queue = new ConcurrentQueue<string>();
queue.Enqueue(item);
queue.TryDequeue(out var result);

0

just kim님의 프로필 이미지
just kim
질문자

헉 링크에 들어가보니 페이지가 없네요 ... ㅠ.ㅠ

0

안녕하세요, 인프런 AI 인턴입니다. just kim님.

질문에서 언급하신 Thread.Join() 및 관련된 쓰레드 처리와 UI 멈춤 현상에 대해 설명드리겠습니다.

  1. Thread.Join()의 영향:

    • Thread.Join() 메서드는 지정된 스레드가 완료될 때까지 현재 스레드의 실행을 차단합니다. WinForms에서 이 코드를 UI 스레드에서 호출하면, UI가 멈춘 것처럼 보이게 됩니다. 그 이유는 UI 스레드가 Join() 호출로 인해 대기 상태에 들어가게 되면서 다른 UI 이벤트를 처리할 수 없기 때문입니다.
    • UI 응답성을 유지하려면 Join()을 UI 스레드가 아닌 다른 작업 스레드에서 사용하거나, 더불어 비동기 메서드를 활용하여 작업 완료를 기다리는 방법을 고려해야 합니다.
  2. 스레드에서 null 할당:

    • 스레드를 종료하는 방법은 null을 할당하기보다는 스레드 작업이 자연스럽게 종료되도록 설계하는 것이 중요합니다. Thread.Join()을 사용하여 스레드가 종료할 때까지 기다린 뒤 이후 작업을 수행하는 방식으로 관리할 수 있습니다.
    • 스레드를 중간에 강제로 종료하는 것은 안전하지 않으며, 일반적으로 작업 완료 확인 후 관련 변수에 null을 할당하여 스레드가 더 이상 참조되지 않도록 하는 방식이 사용됩니다.
  3. 클래스의 소멸:

    • C#에서는 Garbage Collector가 비관리 리소스를 관리하기 때문에, C++의 delete와 같은 명시적인 해제 방법은 없습니다. 대신, 구현된 클래스가 IDisposable 인터페이스를 상속받아 리소스 정리를 수동으로 관리할 수 있습니다.
    • 클래스 인스턴스에서 Dispose() 메서드가 호출되도록 하고, 필요한 경우 그 안에 자원 해제를 구현할 수 있습니다.

이와 관련한 더 자세한 정보를 원하신다면 다음 링크를 참고하세요:
- C#에서 Thread 사용과 UI 응답성 유지하기
- IDisposable 인터페이스와 객체 소멸자 사용하기

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다.
현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏
추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

just kim님의 프로필 이미지
just kim

작성한 질문수

질문하기