어떤 값을 받아들이고 다른 값을 반환하고 함수 외부의 것을 방해하지 않고 부작용이 없으므로 스레드로부터 안전합니다. 함수가 실행되는 방식이 전력 소비에 어떤 영향을 미치는지 고려하고 싶다면 다른 문제입니다.
구현 세부 사항과 관련이없는 일종의 잘 정의 된 프로그래밍 언어를 실행하는 Turing-complete 시스템을 참조한다고 가정합니다. 다시 말해서, 내가 선택한 프로그래밍 언어로 작성하는 함수가 언어의 범위 내에서 불변성을 보장 할 수 있다면 스택이 무엇을하고 있는지는 중요하지 않습니다. 고급 언어로 프로그래밍 할 때 스택에 대해 생각하지 않아도됩니다.
이것이 어떻게 작동하는지 설명하기 위해 C #에서 몇 가지 간단한 예제를 제공 할 것입니다. 이러한 예가 사실이 되려면 몇 가지 가정을해야합니다. 첫째, 컴파일러는 오류없이 C # 사양을 따르고 둘째는 올바른 프로그램을 생성한다는 것입니다.
문자열 컬렉션을 허용하고 컬렉션의 모든 문자열을 쉼표로 구분하여 연결 한 문자열을 반환하는 간단한 함수를 원한다고 가정 해 봅시다. C #에서 단순하고 순진한 구현은 다음과 같습니다.
public string ConcatenateWithCommas(ImmutableList<string> list)
{
string result = string.Empty;
bool isFirst = false;
foreach (string s in list)
{
if (isFirst)
result += s;
else
result += ", " + s;
}
return result;
}
이 예제는 변경할 수 없습니다. 어떻게 알 수 있습니까? string
객체는 불변 이기 때문에 . 그러나 구현이 이상적이지 않습니다. result
변경할 수 없기 때문에 루프를 통해 매번 새로운 문자열 객체를 작성하여 result
가리키는 원래 객체를 교체해야 합니다. 이것은 여분의 스트링을 모두 청소해야하기 때문에 속도에 부정적인 영향을 미치고 가비지 컬렉터에 압력을 가할 수 있습니다.
이제 내가 이것을한다고 가정 해 봅시다.
public string ConcatenateWithCommas(ImmutableList<string> list)
{
var result = new StringBuilder();
bool isFirst = false;
foreach (string s in list)
{
if (isFirst)
result.Append(s);
else
result.Append(", " + s);
}
return result.ToString();
}
string
result
변경 가능한 객체 () 로 교체되었습니다 StringBuilder
. 이다 많은 새로운 문자열이 루프를 통해 매번 생성하지 않기 때문에, 빠른 첫 번째 예보다. 대신 StringBuilder 객체는 각 문자열의 문자를 문자 모음에 추가하고 모든 것을 마지막에 출력합니다.
StringBuilder가 변경 가능하더라도이 함수는 변경할 수 없습니까?
그렇습니다. 왜? 때문에 때마다이 함수가 호출 될 때, 새의 StringBuilder는 그냥 호출에 대해 생성됩니다. 이제 스레드 안전하지만 변경 가능한 구성 요소를 포함하는 순수한 함수가 있습니다.
하지만 내가 이걸하면 어쩌지?
public class Concatenate
{
private StringBuilder result = new StringBuilder();
bool isFirst = false;
public string ConcatenateWithCommas(ImmutableList<string> list)
{
foreach (string s in list)
{
if (isFirst)
result.Append(s);
else
result.Append(", " + s);
}
return result.ToString();
}
}
이 방법은 스레드 안전합니까? 아닙니다. 왜? 클래스가 내 메소드가 의존하는 상태를 유지하고 있기 때문입니다. 경쟁 조건이 이제 메소드에 존재합니다 : 하나의 스레드가 수정할 수 IsFirst
있지만 다른 스레드가 첫 번째를 수행 할 수 있습니다 Append()
.이 경우 문자열의 시작 부분에 쉼표가 있어야합니다.
왜 이렇게 하시겠습니까? 글쎄, 나는 스레드가 result
순서에 관계없이 또는 스레드가 들어오는 순서에 따라 문자열을 누적하기를 원할 수 있습니다 . 아마도 로거 일지 모릅니다.
어쨌든, 그것을 고치기 위해 lock
메소드의 내부에 문장을 넣었습니다 .
public class Concatenate
{
private StringBuilder result = new StringBuilder();
bool isFirst = false;
private static object locker = new object();
public string AppendWithCommas(ImmutableList<string> list)
{
lock (locker)
{
foreach (string s in list)
{
if (isFirst)
result.Append(s);
else
result.Append(", " + s);
}
return result.ToString();
}
}
}
이제 다시 스레드 안전합니다.
내 불변의 메소드가 스레드로부터 안전하지 못할 수있는 유일한 방법은 메소드가 어떻게 구현의 일부를 누출하는 경우입니다. 이런 일이 일어날 수 있습니까? 컴파일러가 정확하고 프로그램이 올바른 경우에는 아닙니다. 그런 방법에 대한 잠금이 필요할까요? 아니.
동시성 시나리오에서 구현이 유출 될 수있는 방법에 대한 예는 여기를 참조하십시오 .
but everything has a side effect
-아뇨. 어떤 값을 받아들이고 다른 값을 반환하고, 함수 외부의 것을 방해하지 않으며, 부작용이 없으므로 스레드로부터 안전합니다. 컴퓨터가 전기를 사용한다는 것은 중요하지 않습니다. 원한다면 메모리 셀을 때리는 우주 광선에 대해서도 이야기 할 수 있지만, 그 주장을 실용적으로 유지합시다. 함수가 실행되는 방식이 전력 소비에 어떤 영향을 미치는지 고려하고 싶다면 스레드 안전 프로그래밍과 다른 문제입니다.