읽기 17:동시성

#### 소프트웨어 6.005

버그로부터 안전 이해하기 쉬운 변경 준비
오늘 수정 하 고 알 수 없는 미래에 올바른. 미래의 프로그래머와 명확하게 의사 소통,미래의 당신을 포함. 다시 작성하지 않고 변경을 수용하도록 설계되었습니다.

#### 목표+메시지 전달&공유 메모리+프로세스&스레드+시간 슬라이스+경쟁 조건##동시성*동시성*은 여러 계산이 동시에 발생 함을 의미합니다. 동시성은 우리가 그것을 좋아 여부,현대 프로그래밍에 도처에있다:네트워크에서+여러 컴퓨터+여러 응용 프로그램은 하나의 컴퓨터에서 실행+컴퓨터에서 여러 프로세서(오늘,단일 칩에 종종 여러 프로세서 코어)사실,동시성은 현대 프로그래밍에 필수적이다:+웹 사이트는 여러 동시 사용자를 처리해야합니다.+모바일 앱(“클라우드”)서버에 자신의 처리의 일부를 할 필요가있다.+그래픽 사용자 인터페이스는 거의 항상 사용자를 방해하지 않는 백그라운드 작업이 필요합니다. 당신은 여전히 그것을 편집하는 동안 예를 들어,이클립스는 자바 코드를 컴파일합니다.동시성으로 프로그래밍 할 수 있다는 것은 미래에도 여전히 중요 할 것입니다. 프로세서 클럭 속도는 더 이상 증가하지 않습니다. 대신,우리는 칩의 새로운 세대마다 더 많은 코어를 얻고 있습니다. 그래서 미래에는 계산을 더 빨리 실행하려면 계산을 동시 조각으로 나눠야 합니다.##동시 프로그래밍을위한 두 가지 모델 동시 프로그래밍을위한 두 가지 공통 모델이 있습니다:*공유 메모리*및*메시지 전달*.

공유 메모리

**공유 메모리.**동시성의 공유 메모리 모델에서 동시 모듈은 메모리에서 공유 객체를 읽고 씀으로써 상호 작용합니다. 공유 메모리 모델의 다른 예:+에이 과 비 동일한 컴퓨터에 두 개의 프로세서(또는 프로세서 코어)일 수 있으며 동일한 물리적 메모리를 공유합니다.+에이 과 비 같은 컴퓨터에서 실행되는 두 개의 프로그램이 될 수 있습니다,그들이 읽고 쓸 수있는 파일과 공통 파일 시스템을 공유.+ㅏ 과 비 동일한 자바 프로그램에서 두 개의 스레드 일 수 있으며(아래에 스레드가 무엇인지 설명 할 것입니다)동일한 자바 객체를 공유합니다.

메시지 전달

**메시지 전달.**메시지 전달 모델에서 동시 모듈은 통신 채널을 통해 서로 메시지를 보내 상호 작용합니다. 모듈은 메시지를 보내고 각 모듈에 들어오는 메시지는 처리를 위해 대기합니다. 예를 들면 다음과 같습니다:+ㅏ 과 비 네트워크에있는 두 대의 컴퓨터 일 수 있으며 네트워크 연결을 통해 통신 할 수 있습니다. 즉,웹 페이지를 요청하고 웹 페이지 데이터를 다시 보냅니다.+에이 과 비 입력 및 출력이 파이프로 연결된 동일한 컴퓨터에서 실행되는 두 프로그램 일 수 있습니다.##프로세스,스레드,시간 슬라이싱메시지 전달 및 공유 메모리 모델은 동시 모듈이 통신하는 방식에 관한 것입니다. 동시 모듈 자체는 프로세스와 스레드의 두 가지 종류가 있습니다.**프로세스**. 프로세스는 동일한 머신의 다른 프로세스로부터 격리된 실행 중인 프로그램의 인스턴스입니다. 특히,그것은 기계 메모리의 자체 개인 섹션을 가지고 있습니다.프로세스 추상화는*가상 컴퓨터*입니다. 프로그램을 마치 컴퓨터 전체를 가지고 있는 것처럼 느끼게 합니다.네트워크를 통해 연결된 컴퓨터와 마찬가지로 프로세스는 일반적으로 그들 사이에 메모리를 공유하지 않습니다. 프로세스는 다른 프로세스의 메모리 또는 개체에 전혀 액세스 할 수 없습니다. 프로세스간에 메모리를 공유하는 것은 대부분의 운영 체제에서*가능*하지만 특별한 노력이 필요합니다. 이 표준 입력&출력 스트림으로 만들어지기 때문에 대조적으로,새로운 프로세스가 자동으로 메시지 전달을위한 준비,이는’시스템.아웃’과`System.in’당신이 자바에서 사용한 스트림.**스레드**. 스레드는 실행 중인 프로그램 내에서 컨트롤의 궤적입니다. 실행 중인 프로그램의 위치와 반환해야 할 위치로 이어진 메서드 호출 스택으로 생각하십시오.프로세스가 가상 컴퓨터를 나타내는 것처럼 스레드 추상화는*가상 프로세서*를 나타냅니다. 새 스레드를 만드는 것은 프로세스에 의해 표현되는 가상 컴퓨터 내부의 새로운 프로세서를 만드는 것을 시뮬레이션합니다. 이 새로운 가상 프로세서는 동일한 프로그램을 실행하고 프로세스의 다른 스레드와 동일한 메모리를 공유합니다.스레드는 프로세스의 모든 메모리를 공유하기 때문에 스레드는 자동으로 공유 메모리를 준비합니다. 단일 스레드에 전용 인”스레드 로컬”메모리를 얻으려면 특별한 노력이 필요합니다. 또한 큐 데이터 구조를 만들고 사용하여 메시지 전달을 명시 적으로 설정해야합니다. 우리는 미래의 독서에서 그 일을하는 방법에 대해 이야기 할 것입니다.

time-slicing

컴퓨터에 하나 또는 두 개의 프로세서 만있는 동시 스레드를 어떻게 가질 수 있습니까? 프로세서보다 스레드가 많은 경우 동시성은**시간 분할**에 의해 시뮬레이션되며,이는 프로세서가 스레드 사이를 전환한다는 것을 의미합니다. 오른쪽 그림에서는 실제 프로세서가 두 개만 있는 컴퓨터에서 세 개의 스레드가 시간 분할된 방법을 보여 줍니다. 이 그림에서 시간은 아래쪽으로 진행되므로 처음에는 한 프로세서가 스레드 티 1 을 실행하고 다른 프로세서는 스레드 티 2 를 실행 한 다음 두 번째 프로세서가 스레드 티 3 을 실행하도록 전환합니다. 스레드 티 2 는 다음 번에 동일한 프로세서 또는 다른 프로세서에서 슬라이스 할 때까지 일시 중지됩니다.대부분의 시스템에서 시간 슬라이싱은 예측할 수없고 비 결정적으로 발생하므로 스레드가 언제든지 일시 중지되거나 재개 될 수 있습니다.

Java 튜토리얼을 읽:+****(1 페이지)+****(1 페이지): http://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html: http://docs.oracle.com/javase/tutorial/essential/concurrency/runthread.html

mitx:c613ec53e92840a4a506f3062c994673 프로세스를&스레드##공유 메모리 ExampleLet 의 예를 살펴 공유 메모리 시스템입니다. 이 예제의 요점은 미묘한 버그를 가질 수 있기 때문에 동시 프로그래밍이 어렵다는 것을 보여주는 것입니다.

은행 계좌에 대한 공유 메모리 모델

은행에 공유 메모리 모델을 사용하는 현금 기계가 있으므로 모든 현금 기계가 동일한 계정 개체를 읽고 쓸 수 있다고 상상해보십시오.memory.To `자바//모든 현금 기계가 하나의 은행 계좌를 공유한다고 가정:의`균형`변수에 저장된 달러 균형,두 개의 작업`예금`과 단순히 달러를 추가하거나 제거`인출`,하나의 계정에 은행을 단순화하자,잘못 될 수있는 설명개인 정적 지능 균형=0;개인 정적 무효 예금(){균형=균형+1;이 간단한 예에서,모든 거래는 단지 1 달러 예금 뒤에 1 달러 인출이므로 계좌의 잔액은 변경되지 않아야합니다. 하루 종일 우리 네트워크의 각 현금 인출기는 일련의 입금/인출 거래를 처리하고 있습니다.’그래서 하루가 끝날 때,얼마나 많은 현금 기계가 실행 중이거나 얼마나 많은 거래를 처리했는지에 관계없이 계정 잔액이 여전히 0 이 될 것으로 기대해야합니다.그러나 이 코드를 실행하면 하루의 마지막 잔액이*0 이 아니라는 것을 자주 발견합니다. 두 개 이상의`캐시 머신()`호출이 동시에 실행되는 경우(예:동일한 컴퓨터의 개별 프로세서에서)`잔액`은 하루가 끝날 때 0 이 아닐 수 있습니다. 왜 안돼?##인터리빙여기에 일어날 수있는 일이 하나 있습니다. 두 개의 현금 기계,ㅏ 과 비,둘 다 보증금을 동시에 사용하고 있다고 가정합니다. 여기에 어떻게 예금()단계는 일반적으로 낮은 수준의 프로세서 명령어로 분해:“균형 가져 오기(균형=0)추가 1 결과를 다시 쓰기(균형=1)“때 ㅏ 과 비 동시에 실행 중일 때 이러한 저수준 명령어가 서로 인터리브됩니다(일부는 어떤 의미에서 동시 일 수도 있지만 지금은 인터리빙에 대해 걱정합시다):“균형 가져 오기(균형=0)추가 1 결과를 다시 쓰기(균형=1)비 균형 가져 오기(균형=1)비 추가 1 비 결과 다시 쓰기(균형=2)”`이 인터리빙은 괜찮습니다.둘 다 에이 과 비 성공적으로 달러. 그러나 인터리빙이 다음과 같이 보인다면 어떨까요:“잔액(잔액=0)비 잔액(잔액=0)추가 1 비 추가 1 결과 다시 쓰기(잔액=1)비 결과 다시 쓰기(잔액=1)”‘잔액은 이제 1-의 달러가 손실되었습니다! 에이 과 비 둘 다 잔액을 동시에 읽고 별도의 최종 잔액을 계산 한 다음 상대방의 예금을 고려하지 못한 새로운 잔액을 다시 저장하기 위해 경쟁했습니다.##경쟁 조건**경쟁 조건**의 예입니다. 이 프로그램은 자바 바이트코드 프로그램의 갯수를 카운트하고,스크립트의 메인 형식을 합계냅니다,그리고 확인되지 않은 실행 텍스트 파일을 찾습니다..##코드를 조정한다고해서 은행 계좌 코드의 모든 버전이 동일한 경쟁 조건을 나타내는 것은 아닙니다:`자바//버전 1 개인 정적 무효 예금(){균형=균형+1;}개인 정적 무효 인출(){균형=균형-1;}“`자바//버전 2 개인 정적 무효 예금(){균형=1;}개인 정적 무효 인출(){++균형;}개인 정적 무효 인출(){–균형;}“당신은 프로세서가 그것을 실행하는 방법을 자바 코드를보고에서 말할 수 없다. 불가분의 연산-원자 연산-이 무엇인지 알 수 없습니다. 그것은 자바의 한 줄이기 때문에 원자 적이 아닙니다. 균형 식별자가 줄에서 한 번만 발생하기 때문에 균형을 한 번만 만지지 않습니다. 자바 컴파일러,그리고 실제로 프로세서 자체는 코드에서 생성 할 낮은 수준의 작업에 대해 약속하지 않습니다. 사실,전형적인 현대 자바 컴파일러는 이러한 세 가지 버전 모두에 대해 정확히 동일한 코드를 생성합니다!핵심 교훈은 경쟁 조건으로부터 안전 할 것인지 여부를 표현을보고 말할 수 없다는 것입니다.

읽기:****(단 1 페이지): http://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html

## 재주문 그것은 사실,그것보다 더 나쁘다. 은행 계좌 잔액의 경쟁 조건은 서로 다른 프로세서에서 순차적 인 작업의 서로 다른 인터리빙 측면에서 설명 할 수 있습니다. 그러나 실제로 여러 변수와 여러 프로세서를 사용할 때 동일한 순서로 나타나는 변수의 변경 사항조차 믿을 수 없습니다.예를 들면 다음과 같습니다:“자바개인적 부울 준비=거짓;개인 지능 대답=0;//컴퓨터 대답이 하나의 스레드에서 실행됩니다.준비){스레드.수율(); “답변이 준비되지 않았습니다!`);}”`우리는 다른 스레드에서 실행되는 두 가지 방법이 있습니다. ‘컴퓨터 응답`은 긴 계산을 수행하여 마침내 대답 42 를 생각해 내고 대답 변수에 넣습니다. 그런 다음 다른 스레드에서 실행 중인 메서드인`응답 사용`에 대한 응답이 사용할 준비가 되었음을 알리기 위해`준비`변수를 참 으로 설정합니다. 코드를 보면 준비가 설정되기 전에’답변`이 설정되므로’사용 응답’이`준비’를 사실로보고 나면`답변`이 42 가 될 것이라고 가정 할 수 있다는 것이 합리적 인 것 같습니다. 그렇지 않아.문제는 현대 컴파일러와 프로세서가 코드를 빠르게 만들기 위해 많은 일을한다는 것입니다. 그 중 하나는 답변과 같은 변수의 임시 복사본을 만들고 빠른 저장소(프로세서의 레지스터 또는 캐시)에서 준비하고 일시적으로 작업하여 결국 메모리의 공식 위치로 다시 저장하는 것입니다. 저장소 저장은 코드에서 변수가 조작된 순서와 다른 순서로 발생할 수 있습니다. 여기에 커버(하지만 명확하게 자바 구문으로 표현)에서 진행 될 수 있습니다 무엇. 프로세서가 효과적으로 만드는 두 개의 변수 임시,`tmpr`과`tmpa`,을 조작 필드를 준비 및 대답:“javaprivate void computeAnswer(){boolean tmpr=준비;int tmpa=응답;tmpa=42;tmpr=true;준비=tmpr;//<–으면 어떻게 될 useAnswer()인터리브 되요? / 메시지 전달 예

메시지 전달 은행 계좌 예

이제 우리 은행 계좌 예제에 대한 메시지 전달 방식을 살펴 보겠습니다.지금 뿐만 아니라 현금 인출기 단위는 이다,그러나 계정은 단위,너무 이다. 모듈은 서로 메시지를 보내 상호 작용합니다. 들어오는 요청은 한 번에 하나씩 처리되도록 큐에 배치됩니다. 발신자는 요청에 대한 답변을 기다리는 동안 작동을 멈추지 않습니다. 자체 대기열에서 더 많은 요청을 처리합니다. 그 요청에 대한 응답은 결국 다른 메시지로 돌아옵니다.불행히도 메시지 전달은 경쟁 조건의 가능성을 제거하지 않습니다. 각 계정이 해당 메시지와 함께`잔액 가져 오기`및`인출`작업을 지원한다고 가정합니다. 두 사용자,현금 인출기 에이 과 비,둘 다 동일한 계좌에서 1 달러를 인출하려고합니다. 당좌 대월이 큰 은행 처벌을 유발하기 때문에 그들은,계정이 보유보다 더 철회 결코 확인하기 위해 먼저 잔액을 확인:“얻을 균형 균형>=1 다음 1 철회”‘문제가 다시 인터리빙되지만,이 시간 인터리빙*메시지*은행 계좌로 전송보다는*명령*에 의해 실행. 계정이 그것에 있는 달러로 시작하는 경우에,메시지의 무슨 인터리빙은 생각으로에 속일 것인가 에이 과 비 둘 다 달러를 철수해서 좋다는 것을,그로 인하여 계정을 초과 인출해서?여기서 한 가지 교훈은 메시지 전달 모델의 작업을 신중하게 선택해야한다는 것입니다. ‘충분한 자금 인출’은`인출`보다 더 나은 작업이 될 것입니다.##동시성은 테스트 및 디버깅하기가 어렵습니다.동시성이 까다 롭다는 것을 설득하지 않았다면 여기에 최악의 상황이 있습니다. 테스트를 사용하여 경쟁 조건을 발견하는 것은 매우 어렵습니다. 그리고 한 번 테스트 버그를 발견 했다,그것을 일으키는 프로그램의 부분에 지역화 하는 것이 매우 어려울 수 있습니다..동시성 버그는 재현성이 매우 떨어집니다. 같은 방식으로 두 번 일어나는 것은 어렵습니다. 명령 또는 메시지의 인터리빙은 환경의 영향을 많이 받는 이벤트의 상대적인 타이밍에 따라 달라집니다. 지연은 실행중인 다른 프로그램,다른 네트워크 트래픽,운영 체제 스케줄링 결정,프로세서 클럭 속도의 변화 등으로 인해 발생할 수 있습니다. 경쟁 조건이 포함된 프로그램을 실행할 때마다 다른 동작이 발생할 수 있습니다. 이러한 종류의 버그는**하이젠 버그**입니다.이 버그는 비결정적이며 재현하기 어렵습니다.”보어 버그”와는 달리 볼 때마다 반복적으로 나타납니다. 순차 프로그래밍의 거의 모든 버그는 보어 버그입니다.당신이 그것을 볼 때 하이젠 버그도 사라질 수 있습니다`인쇄`또는`디버거`! 그 이유는 인쇄 및 디버깅이 다른 작업보다 훨씬 느리기 때문에 종종 100~1000 배 느려서 작업 타이밍을 크게 변경하고 인터리빙하기 때문입니다. 그래서 간단한 인쇄 문을 현금 기계에 삽입():”이 응용 프로그램은 당신이 당신의 돈을 인출 할 수 있습니다,당신은 당신의 돈을 인출 할 수 있습니다.”———–밖으로.버그가 사라지게 만듭니다! }}“`…그리고 갑자기 잔액이 항상 0 이되고 버그가 사라지는 것처럼 보입니다. 그러나 그것은 단지 마스크,진정으로 고정되지 않습니다. 프로그램의 다른 곳에서 타이밍을 변경하면 갑자기 버그가 다시 나타날 수 있습니다.동시성 바로 얻을 어렵다. 이 독서의 요점 중 하나는 당신을 조금 놀라게하는 것입니다. 다음 몇 가지 판독을 통해,우리는 그들이 버그 이러한 종류의 안전 있도록 동시 프로그램을 설계하는 원칙적인 방법을 볼 수 있습니다.동시성##요약+동시성:동시에 실행되는 여러 계산+공유 메모리&메시지 전달 패러다임+프로세스&스레드+프로세스는 가상 컴퓨터와 같습니다; 스레드는 가상 프로세서+경쟁 조건+결과의 정확성(사후 조건 및 불변량)이 이벤트의 상대적인 타이밍에 달려있을 때와 같습니다.이 아이디어는 좋은 소프트웨어의 세 가지 주요 속성에 주로 나쁜 방식으로 연결됩니다. 동시성이 필요하지만 정확성에 심각한 문제가 발생합니다. 우리는 다음 몇 가지 판독에서 이러한 문제를 해결 작동합니다.+**버그로부터 안전합니다.**동시성 버그는 찾기 및 수정하기 가장 어려운 버그 중 일부이며 피하기 위해 신중한 디자인이 필요합니다.+**이해하기 쉽습니다.**동시 코드가 다른 동시 코드와 인터리브하는 방법을 예측하는 것은 프로그래머가 매우 어렵습니다. 프로그래머가 그것에 대해 생각할 필요가 없도록 설계하는 것이 가장 좋습니다. +**변화를위한 준비.**여기에 특히 관련이 없습니다.

답글 남기기

이메일 주소는 공개되지 않습니다.