"스레드 안전"코드 란 무엇입니까?
두 개의 스레드가 기본 데이터를 동시에 변경할 수 없다는 것을 의미합니까? 아니면 둘 이상의 스레드가 실행 중일 때 주어진 코드 세그먼트가 예측 가능한 결과로 실행된다는 것을 의미합니까?
Wikipedia에서 :
스레드 안전성은 다중 스레드 프로그램의 맥락에서 적용 가능한 컴퓨터 프로그래밍 개념입니다. 여러 스레드가 동시에 실행하는 동안 올바르게 작동하면 코드 조각이 스레드로부터 안전합니다. 특히, 여러 공유 스레드가 동일한 공유 데이터에 액세스 할 필요성과 주어진 시간에 하나의 스레드 만 공유 데이터 조각에 액세스 할 필요성을 충족시켜야합니다.
스레드 안전을 달성하는 몇 가지 방법이 있습니다.
재입국 :
한 작업에서 부분적으로 실행하고 다른 작업에서 다시 입력 한 다음 원래 작업에서 다시 시작할 수 있도록 코드를 작성합니다. 이를 위해서는 정적 또는 전역 변수 대신 각 작업의 로컬 변수, 일반적으로 스택에 상태 정보를 저장해야합니다.
상호 배제 :
공유 데이터에 대한 액세스는 언제든지 하나의 스레드 만 공유 데이터를 읽거나 쓰도록하는 메커니즘을 사용하여 직렬화됩니다. 코드 조각이 여러 공유 데이터 조각에 액세스하는 경우 세심한주의가 필요합니다. 문제는 경쟁 조건, 교착 상태, 라이브 록, 기아 및 많은 운영 체제 교과서에 열거 된 다양한 기타 문제를 포함합니다.
스레드 로컬 스토리지 :
각 스레드마다 고유 한 개인 사본이 있도록 변수가 현지화됩니다. 이러한 변수는 서브 루틴 및 기타 코드 경계에 걸쳐 값을 유지하며, 액세스하는 코드가 재진입 될 수 있더라도 각 스레드에 대해 로컬이므로 스레드 안전합니다.
원자력 운영 :
다른 스레드에 의해 중단 될 수없는 원자 연산을 사용하여 공유 데이터에 액세스합니다. 일반적으로 런타임 라이브러리에서 사용 가능한 특수 기계 언어 명령어를 사용해야합니다. 작업이 원 자성이므로 다른 스레드가 액세스하는 내용에 관계없이 공유 데이터는 항상 유효한 상태로 유지됩니다. 원자 연산은 많은 스레드 잠금 메커니즘의 기초를 형성합니다.
더 읽기 :
http://en.wikipedia.org/wiki/Thread_safety
프랑스어로 : http://fr.wikipedia.org/wiki/Threadsafe (매우 짧음)
스레드 안전 코드는 많은 스레드가 동시에 실행하더라도 작동하는 코드입니다.
http://mindprod.com/jgloss/threadsafe.html
더 유익한 질문은 코드가 스레드에 안전하지 않게 만드는 것입니다. 그리고 정답은 4 가지 조건이 있어야한다는 것입니다. 다음 코드를 상상해보십시오 (기계 언어 번역).
totalRequests = totalRequests + 1
MOV EAX, [totalRequests] // load memory for tot Requests into register
INC EAX // update register
MOV [totalRequests], EAX // store updated value back to memory
- 첫 번째 조건은 둘 이상의 스레드에서 액세스 할 수있는 메모리 위치가 있다는 것입니다. 일반적으로 이러한 위치는 전역 / 정적 변수이거나 전역 / 정적 변수에서 도달 할 수있는 힙 메모리입니다. 각 스레드는 함수 / 메소드 범위의 지역 변수에 대한 자체 스택 프레임을 가져 오므로 스택에있는 이러한 로컬 함수 / 메소드 변수 otoh는 해당 스택을 소유 한 하나의 스레드에서만 액세스 할 수 있습니다.
- 두 번째 조건은 이러한 공유 메모리 위치와 연관된 특성 (종종 불변 이라고 함 )이 있으며 프로그램이 올바르게 작동하기 위해서는 true 또는 유효해야한다는 것입니다. 위의 예에서 속성은 " totalRequests는 모든 스레드가 증가 명령문의 일부를 실행 한 총 횟수를 정확하게 나타내야합니다 ". 일반적으로 업데이트가 올바르게 수행 되려면 업데이트가 발생하기 전에이 불변 속성이 true를 유지해야합니다 (이 경우 totalRequests는 정확한 수를 보유해야 함).
- 세 번째 조건은 실제 업데이트의 일부 부분에서 고정 속성이 유지되지 않는 것입니다. (일부 처리 중에 일시적으로 유효하지 않거나 거짓입니다). 이 특정 경우, totalRequests가 페치 된 시간부터 업데이트 된 값이 저장 될 때까지 totalRequests는 불변을 만족 시키지 않습니다 .
- 레이스가 발생하고 코드 가 "스레드 안전"상태가 되지 않기 위해 발생해야하는 네 번째이자 마지막 조건 은 불변이 깨지는 동안 다른 스레드가 공유 메모리에 액세스 할 수 있어야 하므로 불일치 또는 잘못된 행동.
나는 Brian Goetz의 Java Concurrency의 정의가 포괄적이라는 점을 좋아합니다.
"클래스는 런타임 환경에 의한 스레드 실행의 스케줄링 또는 인터리빙에 관계없이 호출 스레드의 일부에 대한 추가 동기화 또는 기타 조정없이 여러 스레드에서 액세스 될 때 올바르게 동작하는 경우 스레드로부터 안전합니다. "
다른 사람들이 지적했듯이 스레드 안전은 한 번에 두 개 이상의 스레드가 사용하는 경우 코드가 오류없이 작동한다는 것을 의미합니다.
이것은 때때로 컴퓨터 시간과 복잡한 코딩 비용이 들기 때문에 항상 바람직한 것은 아닙니다. 하나의 스레드에서만 클래스를 안전하게 사용할 수 있다면 그렇게하는 것이 좋습니다.
예를 들어, 자바는 거의 동등한 두 개의 클래스를 가지고 StringBuffer
와 StringBuilder
. 차이점은 StringBuffer
스레드로부터 안전하므로 StringBuffer
한 번에 여러 스레드에서 단일 인스턴스를 사용할 수 있다는 것입니다. StringBuilder
스레드로부터 안전하지 않으며, 하나의 스레드만으로 문자열을 빌드 할 때 이러한 경우 (대부분의 경우)의 고성능 대체물로 설계되었습니다.
스레드 안전 코드는 다른 스레드가 동시에 입력하더라도 지정된대로 작동합니다. 이는 종종 중단없이 실행해야하는 내부 데이터 구조 또는 작업이 동시에 다른 수정으로부터 보호됨을 의미합니다.
이해하기 쉬운 방법은 코드를 스레드로부터 안전하지 않게 만드는 것입니다. 스레드 응용 프로그램이 원치 않는 동작을하게하는 두 가지 주요 문제가 있습니다.
잠금없이 공유 변수에 액세스이 변수
는 기능을 실행하는 동안 다른 스레드에서 수정할 수 있습니다. 함수의 동작을 확인하기 위해 잠금 메커니즘으로이를 방지하려고합니다. 일반적으로 가장 짧은 시간 동안 잠금을 유지하는 것이 좋습니다.공유 변수에 대한 상호 종속성으로 인한 교착 상태
두 개의 공유 변수 A와 B가있는 경우 한 함수에서 A를 먼저 잠근 다음 나중에 B를 잠급니다. 다른 함수에서는 B를 잠그고 잠시 후 A를 잠급니다. 는 두 번째 기능이 A 잠금 해제를 기다릴 때 첫 번째 기능이 B 잠금 해제를 기다릴 수있는 잠재적 교착 상태입니다. 이 문제는 개발 환경에서 발생하지 않으며 때때로 만 발생합니다. 이를 방지하려면 모든 잠금 장치는 항상 같은 순서로되어 있어야합니다.
예, 아니오
스레드 안전성은 한 번에 하나의 스레드 만 공유 데이터에 액세스 할 수 있도록하는 것보다 조금 더 중요합니다. 경쟁 조건 , 교착 상태 , 라이브 록 및 리소스 부족을 피하면서 동시에 공유 데이터에 순차적으로 액세스해야 합니다.
여러 스레드가 실행되고 예측할 수없는 결과입니다 하지 스레드 안전 코드의 필요 조건이지만 종종 부산물이다. 예를 들어, 공유 큐, 하나의 생산자 스레드 및 소수의 소비자 스레드 로 생산자-소비자 구성표를 설정할 수 있으며 데이터 흐름을 완벽하게 예측할 수 있습니다. 더 많은 소비자를 소개하기 시작하면 더 무작위로 보이는 결과가 나타납니다.
본질적으로, 다중 스레드 환경에서 많은 것들이 잘못 될 수 있습니다 (명령 순서 변경, 부분적으로 구성된 객체, CPU 레벨에서의 캐싱으로 인해 다른 스레드에서 다른 값을 갖는 동일한 변수).
실제로 Java Concurrency에서 제공 한 정의가 마음 에 듭니다 .
[코드의 일부]는 런타임 환경에서 해당 스레드의 실행을 예약 또는 인터리빙하고 여러 스레드에서 액세스 할 때 올바르게 동작하는 경우 스레드로부터 안전합니다. 호출 코드.
하여 제대로 그들은 그것의 사양을 준수하는 프로그램 동작합니다을 의미한다.
고안된 예
카운터를 구현한다고 상상해보십시오. 다음과 같은 경우 올바르게 작동한다고 말할 수 있습니다.
counter.next()
이전에 이미 반환 된 값을 반환하지 않습니다 (간단 성을 위해 오버플로 등이 없다고 가정 함)- 0에서 현재 값까지의 모든 값이 특정 단계에서 리턴되었습니다 (값을 건너 뛰지 않음)
스레드 안전 카운터는 동시에 액세스하는 스레드 수에 관계없이 이러한 규칙에 따라 작동합니다 (일반적으로 순진한 구현에는 해당되지 않음).
참고 : 프로그래머의 교차 게시물
많은 스레드가 동시에이 코드를 실행하는 경우 코드가 제대로 실행됩니다.
다른 좋은 답변 위에 더 많은 정보를 추가하고 싶습니다.
스레드 안전성은 여러 스레드가 메모리 불일치 오류없이 동일한 객체에서 데이터를 쓰거나 읽을 수 있음을 의미합니다. 다중 스레드 프로그램에서 스레드 안전 프로그램은 공유 데이터에 부작용을 일으키지 않습니다 .
자세한 내용은이 SE 질문을 살펴보십시오.
스레드 안전 프로그램은 메모리 일관성을 보장합니다 .
고급 동시 API의 Oracle 설명서 페이지 에서 :
메모리 일관성 속성 :
Java ™ 언어 스펙의 17 장은 공유 변수의 읽기 및 쓰기와 같은 메모리 조작과 관련하여 발생하는 관계를 정의합니다. 하나의 스레드에 의한 쓰기 결과는 읽기 작업 이전에 쓰기 작업이 발생하는 경우에만 다른 스레드가 읽을 수 있도록 보장됩니다 .
synchronized
및 volatile
구조뿐만 아니라,로 Thread.start()
및 Thread.join()
방법, 캔 형상이 발생하기 전에- 관계.
모든 클래스 java.util.concurrent
와 그 하위 패키지 의 메소드는 이러한 보증을 더 높은 수준의 동기화 로 확장 합니다. 특히:
- 객체를 동시 수집에 배치하기 전에 스레드에서 수행하는 작업은 다른 스레드의 컬렉션에서 해당 요소를 액세스하거나 제거한 후에 수행됩니다.
- 실행이 시작되기 전에 a
Runnable
에 제출하기 전에 스레드의 조치Executor
. 마찬가지로가 제출 된 Callable에 대해서도 마찬가지입니다ExecutorService
. - 비동기 계산에 의해 취해진 조치 는 다른 스레드
Future
를 통해 결과를 검색 한 후 발생 하는 조치로 표시됩니다Future.get()
. - 다른 스레드의 동일한 동기화 기 객체에서 와 같이 성공적인 "획득"방법에 후속하는 사전 조치 와 같은 동기화 기 메소드 를 "릴리스"
Lock.unlock, Semaphore.release, and CountDownLatch.countDown
하기 전의 조치Lock.lock, Semaphore.acquire, Condition.await, and CountDownLatch.await
. - 를 통해 객체를 성공적으로 교환하는 각 스레드 쌍 에 대해 각 스레드의
Exchanger
이전 작업exchange()
은 다른 스레드의 해당 exchange () 이후의 작업보다 먼저 발생합니다. - 호출하기 전의 조치 (
CyclicBarrier.await
및Phaser.awaitAdvance
그 변형)는 배리어 조치에 의해 수행 된 조치 이전에 발생하고 배리어 조치에 의해 수행 된 조치는 해당 스레드에서 성공적으로 리턴 된 조치 후에 다른 스레드에서 대기합니다.
스레드 안전성을 결정론과 혼동하지 마십시오. 스레드 안전 코드는 비 결정적 일 수도 있습니다. 스레드 코드로 문제를 디버깅하는 데 어려움이 있다면 이것은 아마도 정상적인 경우 일 것입니다. :-)
스레드 안전성은 스레드가 공유 데이터를 수정하거나 읽을 때 다른 스레드가 데이터를 변경하는 방식으로 액세스 할 수 없도록합니다. 코드가 정확한 실행 순서에 의존하는 경우이를 보장하기 위해 스레드 안전에 필요한 것 이외의 다른 동기화 메커니즘이 필요합니다.
다른 답변을 완료하려면
메소드의 코드가 다음 두 가지 중 하나를 수행 할 때만 동기화가 걱정됩니다.
- 스레드 안전하지 않은 외부 리소스와 함께 작동합니다.
- 영구 객체 또는 클래스 필드를 읽거나 변경
즉, 메소드 내에 정의 된 변수는 항상 스레드 안전합니다. 메소드에 대한 모든 호출에는 이러한 변수의 자체 버전이 있습니다. 메소드가 다른 스레드 또는 동일한 스레드에 의해 호출되거나 메소드가 자체 호출 (재귀)하는 경우에도 이러한 변수의 값은 공유되지 않습니다.
스레드 스케줄링은 라운드 로빈 일 수 없습니다 . 작업은 동일한 우선 순위의 스레드를 희생하여 CPU를 완전히 비울 수 있습니다. Thread.yield ()를 사용하여 양심을 가질 수 있습니다. (Java에서) Thread.setPriority (Thread.NORM_PRIORITY-1)를 사용하여 스레드의 우선 순위를 낮출 수 있습니다
또한 다음을주의하십시오 :
- 이러한 "스레드 안전"구조를 반복하는 응용 프로그램에 대한 많은 런타임 비용 (이미 다른 사용자가 언급 함).
- Thread.sleep (5000)은 5 초 동안 휴면 상태입니다. 그러나 누군가가 시스템 시간을 변경하면 아주 오랫동안 잠을 자거나 전혀 시간을 보내지 않을 수 있습니다. OS는 웨이크 업 시간을 상대 형식이 아닌 절대 형식으로 기록합니다.
그렇습니다. 데이터가 둘 이상의 스레드에 의해 동시에 수정되지 않음을 의미합니다. 그러나 프로그램이 예상대로 작동하고 기본적으로 안전하지 않더라도 스레드로부터 안전 해 보일 수 있습니다.
결과의 예측 불가능 성은 데이터가 예상 된 순서 이외의 순서로 수정 될 수있는 '인종 조건'의 결과입니다.
예를 들어 이것에 대답하자 :
class NonThreadSafe {
private int counter = 0;
public boolean countTo10() {
count = count + 1;
return (count == 10);
}
이 countTo10
메서드는 카운터에 하나를 추가 한 다음 카운트가 10에 도달하면 true를 반환합니다. 한 번만 true를 반환해야합니다.
하나의 스레드 만 코드를 실행하는 한 작동합니다. 두 개의 스레드가 동시에 코드를 실행하면 다양한 문제가 발생할 수 있습니다.
예를 들어, count가 9로 시작하면 한 스레드가 1을 계산하여 계산 (10 만들기) 할 수 있지만 두 번째 스레드가 메소드를 입력하고 1을 다시 추가하여 (11 만들기) 첫 번째 스레드가 10과의 비교를 실행할 수 있습니다. 그런 다음 두 스레드가 비교를 수행하고 count가 11이고 true를 반환하지 않는다는 것을 알게됩니다.
따라서이 코드는 스레드로부터 안전하지 않습니다.
본질적으로 모든 멀티 스레딩 문제는 이러한 종류의 문제의 변형으로 인해 발생합니다.
해결책은 추가 및 비교를 분리 할 수 없도록하는 것입니다 (예를 들어 두 종류의 동기화 코드로 두 명령문을 묶음). 또는 두 개의 조작이 필요하지 않은 솔루션을 고안 할 수 있습니다. 이러한 코드는 스레드로부터 안전합니다.
가장 간단한 단어 : P 코드 블록에서 여러 스레드를 실행하는 것이 안전하면 스레드 안전합니다 *
* 조건이 적용됩니다
조건은 1과 같은 다른 답변으로 언급됩니다. 결과는 하나의 스레드 또는 여러 스레드를 실행하는 경우 동일해야합니다.
참고 URL : https://stackoverflow.com/questions/261683/what-is-meant-by-thread-safe-code
'Programming' 카테고리의 다른 글
Windows에서 Git : mergetool을 어떻게 설정합니까? (0) | 2020.03.04 |
---|---|
애플리케이션에서 개인 API 키를 저장하고 보호하는 모범 사례 (0) | 2020.03.04 |
파이썬에서 숫자의 목록을 합치십시오 (0) | 2020.03.04 |
일부는 거절하더라도 모든 약속이 완료 될 때까지 기다리십시오 (0) | 2020.03.04 |
Java에서 StringBuilder를 사용하는 경우 (0) | 2020.03.04 |