멀티 스레딩이 잘못되어 거의 / 실제로 실패한 프로젝트에서 어떤 교훈을 얻었습니까? [닫은]


11

멀티 스레딩이 잘못되어 거의 / 실제로 실패한 프로젝트에서 어떤 교훈을 얻었습니까?

때로는 프레임 워크에 특정 스레딩 모델이 적용되어 일을 처리하기가 더 어려워집니다.

나에 관해서는, 나는 마지막 실패에서 아직 회복하지 못했고 그 프레임 워크에서 멀티 스레딩과 관련된 것을 다루지 않는 것이 낫다고 생각합니다.

포크 / 조인이 간단하고 데이터가 한 방향으로 만 이동하는 (스레딩이 원형 방향으로 이동하는) 멀티 스레딩 문제에 능숙하다는 것을 알았습니다.

일부 작업은 엄격하게 직렬화 된 스레드 ( "주 스레드")에서만 수행 될 수 있고 다른 작업은 기본 스레드 ( "작업자 스레드") 이외의 스레드에서만 수행 될 수있는 GUI를 처리 할 수 ​​없습니다. 데이터와 메시지는 N 구성 요소 (완전히 연결된 그래프) 사이에서 모든 방향으로 이동해야합니다.

그 프로젝트를 다른 프로젝트로 떠났을 때 교착 상태 문제가 도처에있었습니다. 2-3 개월 후 다른 여러 개발자들이 교착 상태 문제를 모두 해결하여 고객에게 배송 할 수있을 정도로 개선했다고 들었습니다. 나는 부족한 지식이 없다는 것을 결코 알지 못했습니다.

프로젝트에 관한 것 : 메시지 ID의 수 (스레딩에 관계없이 다른 객체의 메시지 큐로 전송 될 수있는 이벤트의 의미를 설명하는 정수 값)는 수천으로 나옵니다. 고유 한 문자열 (사용자 메시지)도 약 천 개에 이릅니다.

추가

다른 팀 (내 과거 또는 현재 프로젝트와 무관)에서 얻은 가장 좋은 비유는 "데이터를 데이터베이스에 넣는 것"이었습니다. (중앙 집중화 및 원자 업데이트를 참조하는 "데이터베이스") 모두 동일한 "메인 스레드"에서 실행되는 여러보기로 분할되고 GUI가 아닌 무거운 리프팅은 개별 작업자 스레드에서 수행됩니다. 데이터베이스처럼 작동하는 단일 plase에 저장되고 "데이터베이스"가 사소한 데이터 종속성과 관련된 모든 "원자 업데이트"를 처리하도록합니다. GUI의 다른 모든 부분은 화면 그리기 만 처리합니다. UI 부분은 물건을 캐시 할 수 있으며 제대로 설계 된 경우 1 초가 지나면 사용자가 알지 못합니다. 이 "데이터베이스"는 "문서"라고도합니다. 문서보기 아키텍처. 불행히도-아니요, 내 앱은 실제로 모든 데이터를 Views에 저장합니다. 왜 그런지 모르겠습니다.

동료 기고자 :

(기여자는 실제 / 개인적인 예를 사용할 필요가 없습니다. 일화적인 예에서 얻은 교훈은 자신이 신뢰할 수 있다고 판단되면 환영합니다.)



나는 '실을 생각할'수 있다는 것은 더 나은 표현이 부족하기 때문에 다소 재능이 있고 배울 수있는 것이 아니라고 생각합니다. 병렬 시스템을 오랫동안 사용해 온 많은 개발자를 알고 있지만 데이터가 여러 방향으로 진행되어야하는 경우 질식합니다.
dauphic

답변:


13

내가 가장 좋아하는 수업 – 매우 열심히 이겼습니다! – 멀티 스레드 프로그램에서 스케줄러는 당신을 미워하는 열악한 돼지입니다. 상황이 잘못 될 경우 예상치 못한 방식으로 진행됩니다. 아무것도 잘못을 얻을, 당신은 이상한 Heisenbugs (불확정성 버그)를 쫓고있을거야 (때문에 어떤 당신이 타이밍을 변경하고 당신에게 다른 실행 패턴을 줄 것이다 추가 장비).

이 문제를 해결하는 유일한 방법은 모든 스레드 처리를 작은 코드 조각으로 엄격하게 상관 시키는 것입니다.이 코드는 모든 코드를 올바르게 가져오고 잠금이 올바르게 유지되도록하는 데 매우 보수적입니다 (전 세계적으로 일정한 순서로 수집). . 가장 쉬운 방법은 비동기식 메시징제외하고 스레드간에 메모리 (또는 다른 리소스)를 공유하지 않는 것입니다 . 스레드를 모르는 스타일로 다른 모든 것을 쓸 수 있습니다. (Bonus : 클러스터의 여러 시스템으로 확장하는 것이 훨씬 쉽습니다.)


"비 동기화해야하는 메시징을 제외하고 스레드간에 메모리 (또는 기타 자원)를 공유하지 않으려면 +1";
Nemanja Trifunovic

1
유일한 방법은? 불변의 데이터 타입은 어떻습니까?
Aaronaught

is that in a multithreaded program the scheduler is a sneaky swine that hates you.-아니, 그렇지 않습니다, 그것은 당신이 말한 것을 정확하게 수행합니다 :)
mattnz

@Aaronaught : 불변의 경우에도 참조로 전달 된 전역 값에는 여전히 전역 GC가 필요하며 이는 전체 전역 리소스를 다시 소개합니다. 스레드 별 메모리 관리를 사용할 수 있으면 전체 글로벌 잠금을 제거 할 수 있기 때문에 좋습니다.
Donal Fellows

기본이 아닌 유형의 값을 참조로 전달할 수는 없지만 더 높은 수준의 잠금이 필요합니다 (예 : 일부 메시지가 다시 표시 될 때까지 참조를 보유하는 "소유자"). 또는 소유권을 이전하기위한 메시징 엔진의 복잡한 코드. 또는 모든 스레드를 마샬링하고 다른 스레드에서 마샬링을 해제하면 속도가 훨씬 느려집니다 (어쨌든 클러스터에 갈 때 수행해야 함). 추격을 줄이고 메모리를 전혀 공유하지 않는 것이 더 쉽습니다.
Donal Fellows

6

다음은 현재 내가 생각할 수있는 몇 가지 기본 교훈입니다 (프로젝트 실패가 아니라 실제 프로젝트에서 발생한 실제 문제).

  • 공유 리소스를 보유한 상태에서 통화를 차단하지 마십시오. 일반적인 교착 상태 패턴은 스레드가 뮤텍스를 잡고 같은 뮤텍스에서 콜백 블록을 만드는 것입니다.
  • mutex / critical 섹션으로 공유 데이터 구조에 대한 액세스를 보호하십시오 (또는 잠금없는 구조를 사용하십시오.
  • 원 자성을 가정하지 마십시오. 원자 API를 사용하십시오 (예 : InterlockedIncrement).
  • 사용중인 라이브러리, 객체 또는 API의 스레드 안전성에 관한 RTFM
  • 이벤트, 세마포어와 같은 사용 가능한 동기화 기본 요소를 활용하십시오. (하지만 당신이 당신이 좋은 상태에 있다는 것을 알고있을 때주의를 기울이십시오-나는 이벤트 또는 데이터가 손실 될 수있는 잘못된 상태로 신호를 보낸 많은 예를 보았습니다)
  • 스레드가 동시에 및 / 또는 임의의 순서로 실행될 수 있고 해당 컨텍스트가 언제든지 다른 스레드를 전환 할 수 있다고 가정합니다 (OS에서 다른 보장을하지 않는 한).

6
  • 전체 GUI 프로젝트는 메인 스레드 에서만 호출해야합니다 . 기본적으로 GUI에 단일 ".net" "invoke"를 넣지 않아야합니다. 멀티 스레딩은 느린 데이터 액세스를 처리하는 별도의 프로젝트에 고정되어야합니다.

우리는 GUI 프로젝트가 12 개의 스레드를 사용하는 부분을 물려 받았습니다. 문제는 없습니다. 교착 상태, 경주 문제, 크로스 스레드 GUI 호출 ...


"프로젝트"는 "조립"을 의미합니까? 어셈블리 간 클래스 배포로 인해 스레드 문제가 어떻게 발생하는지 알 수 없습니다.
nikie

내 프로젝트에서는 실제로 어셈블리입니다. 그러나 요점은 해당 폴더의 모든 코드를 예외없이 메인 스레드에서 호출해야한다는 것입니다.
Carra

이 규칙이 일반적으로 적용되지 않는다고 생각합니다. 예, 다른 스레드에서 GUI 코드를 호출해서는 안됩니다. 그러나 클래스를 폴더 / 프로젝트 / 어셈블리에 배포하는 방법은 독립적 인 결정입니다.
nikie

1

Java 5 이상에는 멀티 스레딩 포크 조인 스타일 프로그램을보다 쉽게 ​​처리 할 수있는 실행 프로그램이 있습니다.

그것들을 사용하면 많은 고통을 제거 할 수 있습니다.

(그리고 예, 이것은 프로젝트에서 배웠습니다 :))


1
이 답변을 다른 언어에 적용하려면 가능할 때마다 해당 언어에서 제공하는 고품질 병렬 처리 프레임 워크를 사용하십시오. (하지만 프레임 워크가 정말 훌륭하고 사용 가능한지 여부는 시간 만 알 수 있습니다.)
rwong

1

하드 실시간 임베디드 시스템에 대한 배경 지식이 있습니다. 멀티 스레딩으로 인한 문제가 없는지 테스트 할 수 없습니다. (때로는 존재를 확인할 수 있습니다). 코드가 정확해야합니다. 따라서 모든 스레드 상호 작용에 대한 모범 사례.

  • # 1 규칙 : KISS-실이 필요하지 않으면 실을 돌리지 마십시오. 가능한 한 직렬화하십시오.
  • # 2 규칙 : # 1을 깨지 마십시오.
  • # 3 리뷰를 통해 증명할 수 없다면 맞지 않습니다.

규칙 1의 경우 +1. 다른 스레드가 완료 될 때까지 (기본적으로 메소드 호출) 차단할 프로젝트를 진행하고있었습니다. 다행히도 우리는 그 접근법에 반대하기로 결정했습니다.
Michael K

# 3 FTW. 잠금 타이밍 다이어그램 또는 시간이 걸리는 이유에 대해 궁금해하는 데 몇 달이 걸리는 것을 증명하기 위해 사용하는 모든 것에 어려움을 겪고있는 시간을 보내는 것이 좋습니다.

1

작년에 취한 멀티 스레딩 클래스의 유추가 매우 도움이되었습니다. 스레드 동기화는 교차로 (데이터)가 두 대의 자동차 (스레드)에서 한 번에 사용되는 것을 방지하는 교통 신호와 같습니다. 많은 개발자들이 저지르는 실수는 대부분의 도시를 가로 질러 빨간 불을 켜서 한 대의 자동차가 필요한 정확한 신호를 파악하기가 너무 어렵거나 위험하다고 생각하기 때문에 한 대의 차량을 통과시키는 것입니다. 트래픽이 적을 때는 잘 작동하지만 응용 프로그램이 커질수록 그리드 락으로 이어질 수 있습니다.

그것은 이론적으로 이미 알고있는 것이지만, 그 수업 후에 비유는 실제로 저와 붙어 있었고, 그 후 얼마나 자주 스레딩 문제를 조사하고 하나의 거대한 대기열을 찾거나 변수에 쓰는 동안 모든 곳에서 인터럽트가 비활성화되는 것에 놀랐습니다. 두 개의 스레드 만 사용했거나 뮤텍스가 완전히 리팩토링 될 수있을 때 오랫동안 유지되었습니다.

다시 말해, 스레딩 문제를 피하려고하는 오버 킬로 인해 최악의 스레딩 문제가 발생합니다.


0

다시 시도하십시오.

적어도 저에게는 차이를 만든 것은 연습이었습니다. 멀티 스레드 및 분산 작업을 몇 번 수행 한 후에는 중단됩니다.

디버깅이 실제로 어려운 이유라고 생각합니다. VS를 사용하여 멀티 스레드 코드를 디버깅 할 수 있지만 gdb를 사용해야하면 실제로 완전히 손실됩니다. 아마 내 잘못이야

더 많은 것을 배우고있는 또 다른 것은 잠금이없는 데이터 구조입니다.

프레임 워크를 지정하면이 질문이 실제로 향상 될 수 있다고 생각합니다. 예를 들어 .NET 스레드 풀과 백그라운드 워커는 QThread와 실제로 다릅니다. 플랫폼에는 몇 가지 문제가 있습니다.


각 프레임 워크, 특히 내가 노출되지 않은 프레임 워크에서 배울 사항이 있기 때문에 모든 프레임 워크의 스토리를 듣는 데 관심이 있습니다.
rwong

1
디버거는 다중 스레드 환경에서 거의 쓸모가 없습니다.
Pemdas

이미 문제가 무엇인지 알려주는 멀티 스레드 실행 추적 프로그램이 있지만 문제를 해결하는 데 도움이되지 않습니다. 내 문제의 요점은 "현재 디자인에 따르면, 메시지 X를 이런 방식으로 객체 Y에 전달할 수 없습니다 (시퀀스). 그것은 거대한 대기열에 추가되어야하며 결국 처리 될 것입니다 . 메시지가 적시에 사용자에게 표시 될 수있는 방법이 없습니다. 항상 비동기식으로 발생하고 사용자 를 매우 혼란스럽게 만듭니다. 진행 표시 줄을 추가하거나, 버튼을 취소하거나, 오류 메시지를 ' " 그것을 가지고 있지 ."
rwong 님

0

저수준 모듈에서 고수준 모듈로의 콜백은 반대 순서로 잠금을 획득하기 때문에 매우 악하다는 것을 배웠습니다.


콜백은 악한 것이 아닙니다 ... 실을 끊는 것 이외의 다른 작업은 아마도 악의 근원 일 것입니다. 메시지 대기열에 토큰을 보내지 않은 콜백이 의심됩니다.
Pemdas

f (x) 최소화와 같은 최적화 문제 해결은 f (x) 함수에 대한 포인터를 최적화 프로 시저에 제공함으로써 구현되는데, 이는 최소값을 찾는 동안 "다시 호출"합니다. 콜백없이 어떻게 하시겠습니까?
quant_dev

1
공감대는 없지만 콜백은 악한 것이 아닙니다. 잠금을 잡고 콜백 호출하는 것은 사악합니다. 잠금 또는 대기 여부를 모르는 경우 잠금 내부에 아무 것도 호출하지 마십시오. 콜백뿐만 아니라 가상 함수, API 함수, 다른 모듈의 함수 ( "상위 레벨"또는 "하위 레벨")도 포함됩니다.
nikie

@nikie : 콜백 중에 잠금 유지 해야하는 경우 나머지 API를 재진입 (하드!)하도록 설계하거나 잠금을 보유하고 있다는 사실은 API의 문서화 된 부분이어야합니다 ( 불행히도 때로는 할 수있는 모든 것).
Donal Fellows

@Donal Fellows : 콜백 중에 잠금을 유지 해야하는 경우 디자인 결함이 있다고 말하고 싶습니다. 다른 방법이 없다면, 반드시 문서화하십시오! 콜백이 백그라운드 스레드에서 호출되는지 문서화하는 것처럼. 인터페이스의 일부입니다.
nikie
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.