main ()이 종료되면 분리 된 스레드는 어떻게됩니까?


153

내가 시작한 std::thread다음 detach()스레드를 실행 한다고 가정 하면 스레드 std::thread가 한 번 표현 했지만 스레드가 계속 실행되지 않습니다 .

또한 프로그램에 분리 된 스레드 1 을 결합하기위한 신뢰할 수있는 프로토콜이 없으므로 분리 된 스레드가 main()종료 될 때에도 계속 실행 됩니다.

표준에서 (더 정확하게는 N3797 C ++ 14 초안에서) 아무것도 찾을 수 없으며, 1.10도 30.3도 관련 문구를 포함하지 않아야합니다.

1 또 다른, 아마도 동등한 질문은 "연결 해제 된 스레드를 다시 결합 할 수 있습니까?"입니다. 결합하려는 프로토콜이 무엇이든 스레드가 여전히 실행중인 동안 신호 부분을 수행해야하고 OS 스케줄러가 수신단이 스레드가 실제로 완료되었음을 확실하게 감지 할 수있는 방법이없는 신호 처리 직후에 스레드를 1 시간 동안 휴면 상태로두기로 결정합니다.

부족하면 main()분리 된 스레드가 실행하는 것은 정의되지 않은 동작은 다음 어떤 의 사용은 std::thread::detach()메인 스레드가 종료하지 않는 한 정의되지 않은 동작이다 (2) .

따라서 main()분리 된 스레드가 실행 중이면 정의 된 효과 가 있어야합니다 . 질문 : 어디 합니다 (에 C ++ 표준 이 아닌 POSIX가 아닌 OS 워드 프로세서, ...) 정의 된 효과가 있습니다.

2 분리 된 스레드는 (의미에서 std::thread::join()) 결합 될 수 없습니다 . 분리 된 스레드의 결과를 기다릴 수 있습니다 (예 :에서를 통해 std::packaged_task또는 세마포어 또는 플래그 및 조건 변수를 통해) 스레드가 실행을 완료 한 것은 아닙니다 . 당신이 스레드의 첫 번째 자동 개체의 소멸자에 신호 부분을 넣어하지 않는 사실,이 , 일반적으로 실행되는 코드 (소멸자) 일 시그널링 코드입니다. OS가 메인 스레드가 결과를 소비하고 분리 된 스레드가 소멸자를 실행하기 전에 종료하도록 예약하는 경우 어떻게됩니까?


5
[basic.start.term] / 4 : "호출 std::exit또는 종료 전에 모든 스레드를 종료하는 main것만 으로 는 충분하지만 필수는 아닙니다." (전체 단락이 관련 될 수 있음) [support.start.term] / 8 참조 ( 반환 std::exit시 호출 main)
dyp

답변:


45

원래 질문 "종료시 분리 된 스레드는 어떻게됩니까"에 대한 답변 main()은 다음과 같습니다.

표준이 중지되었다고 말하지 않기 때문에 계속 실행되며, 다른 스레드 나 정적 객체의 (automatic | thread_local) 변수에 닿지 않는 한 잘 정의되어 있습니다.

이것은 스레드 관리자를 정적 객체로 허용 할 수있는 것으로 보입니다 ( [basic.start.term] / 4의 설명 은 포인터에 대한 @dyp 덕분에 많은 것을 말합니다).

정적 객체의 소멸이 완료되면 문제가 발생합니다. 실행은 신호 처리기에서 허용 된 코드 만 실행할 수있는 영역으로 들어갑니다 ( [basic.start.term] / 1, 1 번째 문장 ). C ++ 표준 라이브러리 중 <atomic>라이브러리 ( [support.runtime] / 9, 2 번째 문장 ) 만 해당됩니다. 특히, 그것은 일반적으로, ( 제한적 이지 않기 때문에 신호 처리기에서 사용하기 위해 저장되는지 여부에 따라 구현 정의 됩니다) 제외합니다 .condition_variable<atomic>

이 시점에서 스택을 풀지 않으면 정의되지 않은 동작을 피하는 방법을 찾기가 어렵습니다.

두 번째 질문 "분리 된 스레드를 다시 결합 할 수 있음"에 대한 대답은 다음과 같습니다.

네,와 *_at_thread_exit기능의 가족 ( notify_all_at_thread_exit(), std::promise::set_value_at_thread_exit(), ...).

조건 변수 또는 세마포어 또는 원자 카운터 시그널링 질문 각주 [2]에 언급 된 바와 같이 그것의 실행의 끝 보장의 관점에서 (단독 스레드 가입 불충분 -일어난-이전 의 수신 일반적으로, 예를 들어 notify_all()조건 변수, 특히 자동 및 스레드-로컬 객체의 소멸자 후에 더 많은 코드가 실행되기 때문에, 상기 스레드에 의한 상기 시그널링) .

(스레드가 수행하는 마지막으로 신호 실행 자동으로 thread 로컬 객체의 소멸자가 -일어난 것은 )이 무엇인가 _at_thread_exit기능의 가족을 위해 설계되었습니다.

따라서, 표준이 요구하는 것 이상의 어떤 구현 보증의 부재에서 정의되지 않은 동작을 방지하기 위해, 당신은 (수동)를 가진 분리 된 스레드에 가입해야합니다 _at_thread_exit시그널링을하는 기능 또는 분리 된 쓰레드가 실행할 수 있도록 단지 에 대한 안전 할 것입니다 코드를 시그널 핸들러도 마찬가지입니다.


17
이거 확실하니? 테스트 한 모든 곳 (GCC 5, clang 3.5, MSVC 14)에서 메인 스레드가 종료되면 분리 된 모든 스레드가 종료됩니다.
rustyx

3
문제는 특정 구현이하는 것이 아니라 표준이 정의되지 않은 동작으로 정의하는 것을 피하는 방법입니다.
Jon Spencer

7
이 답변은 정적 변수를 파괴 한 후 프로세스가 남아있는 스레드가 완료되기를 기다리는 일종의 절전 상태가 될 것임을 암시하는 것으로 보입니다. exit정적 개체 삭제, atexit처리기 실행 , 스트림 플러시 등을 마친 후에 는 호스트 환경으로 제어를 반환합니다 (예 : 프로세스 종료). 분리 된 스레드가 여전히 실행 중이고 자체 스레드 외부의 항목을 건드리지 않고 정의되지 않은 동작을 피한 경우 프로세스가 종료 될 때 퍼프 연기로 사라집니다.
Jonathan Wakely

3
만약 당신이 다음 비 ISO C ++ API를 사용하고 OK 경우 main전화 pthread_exit대신 반환 또는 호출 exit하는 프로세스가 마무리 분리 된 스레드를 기다린 다음 전화를하게됩니다 다음 exit마지막 완료된 후.
Jonathan Wakely

3
"표준이 중지되었다고 말하지 않기 때문에 계속 실행됩니다."-> 누군가 컨테이너 프로세스없이 스레드가 어떻게 실행을 계속할 수 있는지 말해 줄 수 있습니까?
Gupta

42

스레드 분리

에 따르면 std::thread::detach:

실행 스레드를 스레드 개체와 분리하여 실행을 독립적으로 계속할 수 있습니다. 스레드가 종료되면 할당 된 모든 자원이 해제됩니다.

보낸 사람 pthread_detach:

pthread_detach () 함수는 스레드가 종료 될 때 스레드에 대한 스토리지를 회수 할 수 있음을 구현에 표시해야합니다. 스레드가 종료되지 않은 경우 pthread_detach ()는 종료되지 않습니다. 동일한 대상 스레드에서 여러 pthread_detach () 호출의 영향은 지정되지 않았습니다.

스레드 분리는 주로 애플리케이션이 스레드가 완료 될 때까지 기다릴 필요가없는 경우 (예 : 프로세스 종료까지 실행해야하는 데몬)에 필요한 리소스 절약을위한 것입니다.

  1. 응용 프로그램 측면 핸들을 해제하려면 : std::thread개체를 조인하지 않고 범위를 벗어날 수 있습니다. 이로 인해 일반적으로 std::terminate()소멸이 발생합니다.
  2. OS가 스레드를 종료하자마자 스레드 특정 리소스 ( TCB )를 자동으로 정리할 수 있도록하기 위해 명시 적으로 지정 했으므로 나중에 스레드 결합에 관심이 없으므로 이미 분리 된 스레드에 참여할 수 없습니다.

죽이는 실

프로세스 종료시 동작은 메인 스레드의 동작과 동일하며 적어도 일부 신호를 포착 할 수 있습니다. 주 스레드의 신호 처리기 호출 내에서 다른 스레드를 결합하거나 종료 할 수 있으므로 다른 스레드가 신호를 처리 할 수 ​​있는지 여부는 그렇게 중요하지 않습니다. (관련 질문 )

이미 언급했듯이 분리 여부에 관계없이 모든 스레드는 대부분의 OS에서 프로세스와 함께 죽습니다 . 프로세스 자체는 신호를 발생 exit()시키거나 호출 하거나 주 기능에서 복귀 함으로써 종료 될 수 있습니다 . 그러나 C ++ 11은 기본 OS의 정확한 동작을 정의 할 수없고 정의하지는 않지만 Java VM 개발자는 이러한 차이를 어느 정도 추상화 할 수 있습니다. AFAIK, 이국적인 프로세스 및 스레딩 모델은 일반적으로 고대 플랫폼 (C ++ 11이 이식되지 않을 것임) 및 특수한 및 / 또는 제한된 언어 라이브러리 구현 및 제한된 언어 지원을 가질 수있는 다양한 임베디드 시스템에서 발견됩니다.

스레드 지원

스레드가 지원되지 않으면 일반 프로세스가 있기 때문에 std::thread::get_id()유효하지 않은 id (기본 생성됨 std::thread::id)를 반환해야 합니다. 일반 프로세스는 실행하기 위해 스레드 객체가 필요하지 않으며의 생성자 std::thread는을 throw해야합니다 std::system_error. 이것이 오늘날의 OS와 함께 C ++ 11을 이해하는 방법입니다. 프로세스에서 메인 스레드를 생성하지 않는 스레딩 지원 OS가있는 경우 알려주십시오.

스레드 제어

적절한 종료를 위해 스레드를 계속 제어해야하는 경우 동기화 기본 요소 및 / 또는 일종의 플래그를 사용하여 스레드를 제어 할 수 있습니다. 그러나이 경우 종료 플래그 다음에 조인을 설정하는 것이 좋습니다. 스레드를 분리하여 복잡성을 증가시킬 필요가 없기 때문에 리소스는 동시에 몇 바이트의 std::thread객체 가 해제되므로 리소스가 해제됩니다 더 높은 복잡성과 더 많은 동기화 프리미티브가 수용 가능해야합니다.


3
모든 스레드에는 자체 스택 (Linux의 경우 메가 바이트 범위)이 있으므로 스레드를 분리하고 (스레드가 종료 되 자마자 스택이 해제되도록) 메인 스레드를 종료 해야하는 경우 일부 동기화 프리미티브를 사용합니다 (그리고 올바르게 종료하려면 리턴 / 종료시 스레드를 종료하는 대신 여전히 실행중인 스레드를 결합해야합니다).
Norbert Bérci

8
이 질문에 어떻게 대답하는지
모르겠습니다.

18

다음 코드를 고려하십시오.

#include <iostream>
#include <string>
#include <thread>
#include <chrono>

void thread_fn() {
  std::this_thread::sleep_for (std::chrono::seconds(1)); 
  std::cout << "Inside thread function\n";   
}

int main()
{
    std::thread t1(thread_fn);
    t1.detach();

    return 0; 
}

Linux 시스템에서이를 실행하면 thread_fn의 메시지가 인쇄되지 않습니다. OS thread_fn()main()종료 하자마자 실제로 정리 됩니다. 교체 t1.detach()와 함께 t1.join()항상 예상대로하면 메시지를 인쇄합니다.


이 동작은 Windows에서 정확히 발생합니다. 따라서 프로그램이 끝나면 Windows가 분리 된 스레드를 종료하는 것으로 보입니다.
Gupta

17

프로그램이 종료 된 후 스레드의 운명은 정의되지 않은 동작입니다. 그러나 최신 운영 체제는 프로세스를 닫을 때 생성 된 모든 스레드를 정리합니다.

를 분리 할 때 std::thread다음 세 가지 조건이 계속 유지됩니다.

  1. *this 더 이상 스레드를 소유하지 않습니다
  2. joinable() 항상 같음 false
  3. get_id() 같을 것이다 std::thread::id()

1
왜 정의되지 않습니까? 표준은 아무것도 정의하지 않기 때문에? 나의 각주에 의해, 그것은 detach()정의되지 않은 행동을 요구하지 않습니까? 믿기 ​​어렵다 ...
Marc Mutz-mmutz

2
@ MarcMutz-mmutz 프로세스가 종료되면 스레드의 운명이 정의되지 않는다는 의미에서 정의되지 않습니다.
카이사르

2
@Caesar 및 스레드가 끝나기 전에 종료되지 않도록하려면 어떻게합니까?
MichalH


0

다른 스레드가 계속 실행되도록하려면 exit (3) 대신 pthread_exit ()를 호출하여 기본 스레드를 종료해야합니다. 메인에서 pthread_exit를 사용하는 것이 좋습니다. pthread_exit를 사용하면 기본 스레드가 실행을 중지하고 다른 모든 스레드가 종료 될 때까지 좀비 (defunct) 상태로 유지됩니다. 기본 스레드에서 pthread_exit를 사용하는 경우 다른 스레드의 리턴 상태를 얻을 수없고 다른 스레드를 정리할 수 없습니다 (pthread_join (3)을 사용하여 수행 할 수 있음). 또한 스레드 종료시 스레드 리소스가 자동으로 해제되도록 스레드 (pthread_detach (3))를 분리하는 것이 좋습니다. 모든 스레드가 종료 될 때까지 공유 리소스가 해제되지 않습니다.


@kgvinod, 왜 "pthread_exit (0);"을 추가하지 않습니까? "ti.detach ()"이후;
yshi

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.