문제

C ++의 멀티 스레딩과 예외 안전 사이의 긴장은 무엇입니까? 따라야 할 좋은 지침이 있습니까? 데치가없는 예외 때문에 스레드가 종료됩니까?

도움이 되었습니까?

해결책

C ++ 표준은 멀티 스레딩에 대한 언급을하지 않는다고 생각합니다. 멀티 스레딩은 플랫폼 별 기능입니다.

C ++ 표준이 일반적으로 매치되지 않은 예외에 대해 무엇을 말하는지 확실하지 않지만 이 페이지, 발생하는 일은 플랫폼 정의이며 컴파일러의 문서에서 찾아야합니다.

빠른 테스트에서 나는 G ++ 4.0.1 (i686-Apple-Darwin8-G ++ -4.0.1)에서 수행 한 결과, 결과는 다음과 같습니다. terminate() 전체 프로그램을 죽인다. 내가 사용한 코드는 다음과 같습니다.

#include <stdio.h>
#include <pthread.h>

void *threadproc(void *x)
{
  throw 0;

  return NULL;
}

int main(int argc, char **argv)
{
  pthread_t t;
  pthread_create(&t, NULL, threadproc, NULL);

  void *ret;
  pthread_join(t, &ret);

  printf("ret = 0x%08x\n", ret);

  return 0;
}

편집 g++ threadtest.cc -lpthread -o threadtest. 출력은 다음과 같습니다.

terminate called after throwing an instance of 'int'

다른 팁

C ++ 0X가 있습니다 스레드 간 예외를 전송하기위한 언어 지원 작업자 스레드가 예외를 던지면 산란 스레드가 그것을 잡거나 재창조 할 수 있습니다.

제안서에서 :

namespace std {

    typedef unspecified exception_ptr;

    exception_ptr current_exception();
    void rethrow_exception( exception_ptr p );

    template< class E > exception_ptr copy_exception( E e );
}

끊임없는 예외는 전화 할 것입니다 terminate() 차례로 terminate_handler (프로그램에서 설정할 수 있음). 기본적으로 terminate_handler 전화 abort().

기본값을 무시하더라도 terminate_handler, 표준에 따르면 귀하가 제공하는 루틴은 "발신자에게 반환하지 않고 프로그램의 실행을 종료해야한다"(ISO 14882-2003 18.6.1.3).

따라서 요약하면, 끊임없는 예외는 스레드뿐만 아니라 프로그램을 종료합니다.

스레드 안전이 진행되는 한 Adam Rosenfield 말하자면, 그것은 표준에 의해 해결되지 않은 플랫폼 특정 것입니다.

이것이 Erlang이 존재하는 가장 큰 이유입니다.

나는 컨벤션이 무엇인지 모르겠지만 IMHO는 가능한 한 Erlang과 비슷합니다. 힙 객체를 불변으로 만들고 스레드간에 통신하기 위해 일종의 메시지 전달 프로토콜을 설정하십시오. 자물쇠를 피하십시오. 메시지 전달이 예외 안전인지 확인하십시오. 스택에 많은 상태를 유지하십시오.

다른 사람들이 논의했듯이, 동시성 (특히 스레드 안전성)은 시스템 설계 및 응용 프로그램을 설계하는 방법에 영향을 미치는 건축 문제입니다.

그러나 예외 안전성과 스레드 안전 사이의 긴장에 대한 질문을하고 싶습니다.

클래스 레벨에서 스레드 안전은 인터페이스를 변경해야합니다. 예외 안전과 마찬가지로. 예를 들어, 클래스가 내부 변수에 대한 참조를 반환하는 것이 일반적입니다.

class Foo {
public:
  void set_value(std::string const & s);

  std::string const & value() const;
};

FOO가 여러 스레드로 공유되면 문제가 기다리고 있습니다. 당연히 FOO에 액세스하기 위해 Mutex 또는 기타 잠금 장치를 넣을 수 있습니다. 그러나 곧 모든 C ++ 프로그래머는 Foo를 "ThreadSafefoo"로 포장하기를 원할 것입니다. 내 논쟁은 Foo의 인터페이스를 다음으로 변경해야한다는 것입니다.

class Foo {
public:
  void set_value(std::string const & s);

  std::string value() const;
};

예, 더 비싸지 만 Foo 내부의 자물쇠로 스레드 안전을 만들 수 있습니다. Imnsho 이것은 스레드 안전성과 예외 안전 사이에 일정량의 긴장을 만듭니다. 또는 적어도 공유 자원으로 사용되는 각 클래스를 두 조명에서 검사해야하므로 더 많은 분석을 수행해야합니다.

한 가지 전형적인 예 (내가 처음 본 곳에서 기억할 수 없음)는 STD 라이브러리에 있습니다.

대기열에서 무언가를 튀는 방법은 다음과 같습니다.

T t;
t = q.front(); // may throw
q.pop();

이 인터페이스는 다음과 비교하여 다소 둔감합니다.

T t = q.pop();

그러나 t 사본 할당이 던질 수 있기 때문에 수행됩니다. 팝이 발생한 후 사본이 던지면 해당 요소는 줄에서 손실되고 절대 복구 할 수 없습니다. 그러나 요소가 튀어 나오기 전에 사본이 발생하므로 Try/Catch 블록의 Front ()에서 사본 주위에 임의의 처리를 할 수 있습니다.

단점은 관련된 두 단계로 인해 std :: queue의 인터페이스로 스레드 안전 인 큐를 구현할 수 없다는 것입니다. 예외 안전 (던질 수있는 단계를 분리)에 좋은 것은 이제 멀티 스레딩에 좋지 않습니다.

예외 안전의 주요 구세주는 포인터 작업이 허랑이라는 것입니다. 마찬가지로, 포인터 작업은 대부분의 플랫폼에서 원자를 만들 수 있으므로 종종 멀티 스레드 코드에서 구세주가 될 수 있습니다. 당신은 당신의 케이크를 먹고 먹을 수 있지만, 정말 어렵습니다.

내가 알아 차린 두 가지 문제가 있습니다.

  • Linux의 G ++에서, 스레드의 킬 링 (pthread_cancel)은 "알 수없는"예외를 던져서 달성됩니다. 한편으로는 실이 죽을 때 잘 정리할 수 있습니다. 반면에, 당신이 그 예외를 포착하고 그것을 재창조하지 않으면, 당신의 코드는 abort ()로 끝납니다. 따라서 귀하 또는 귀하가 사용하는 도서관이 킬 스레드를 사용하는 경우

    잡다(...)

없이

throw;

스레드 코드에서. 여기 웹 에서이 동작에 대한 참조입니다.

  • 때로는 스레드간에 예외를 전송해야합니다. 이것은 쉬운 일이 아닙니다. 적절한 솔루션이 프로세스간에 사용하는 마샬링 /경계의 종류 일 때 우리는 약간의 택을 수행하게되었습니다.

나는 예외를 잡아 당기는 것을 권장하지 않습니다. 프로그램을 더 우아하게 (또는 적어도 구두로) 종료 할 수있는 캐치 핸들러에 최상위 스레드 기능을 감싸십시오.

가장 중요한 것은 다른 스레드의 가입 예외가 사용자에게 표시되거나 기본 스레드에 던져지지 않았다는 것을 기억하는 것입니다. 따라서 시도/캐치 블록이있는 기본 스레드와 다른 스레드에서 실행 해야하는 모든 코드를 뒤틀어 야합니다.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top