문제

ASP.NET 캐시 항목 제거 이벤트에 대한 콜백 함수를 만드는 중입니다.

문서에는 객체에 대해 메서드를 호출해야 한다고 나와 있거나 정적 메서드와 같이 존재할 것이라고 알고 있는 호출(범위 내에 있을 것임)을 호출해야 한다고 나와 있지만 정적 메서드가 스레드로부터 안전한지 확인해야 한다고 나와 있습니다.

1 부:스레드를 안전하게 만들기 위해 내가 할 수 있는 일의 예는 무엇입니까?

2 부:이것은 내가 가지고 있는 경우를 의미합니까?

static int addOne(int someNumber){
    int foo = someNumber;
    return foo +1; 
}

Class.addOne(5)을 호출합니다.및 Class.addOne(6);동시에 누가 foo를 먼저 설정하는지에 따라 6개 또는 7개가 반환될 수 있나요?(즉.경쟁 조건)

도움이 되었습니까?

해결책

저것 Addone 기능은 다른 스레드에서 액세스 할 수있는 데이터에 액세스하지 않기 때문에 실제로 스레드 안전합니다. 각 스레드가 자체 스택을 가져 오기 때문에 로컬 변수는 스레드간에 공유 할 수 없습니다. 그러나 함수 매개 변수는 값 유형이 아니라 참조 유형이 아닌지 확인해야합니다.

static void MyFunction(int x) { ... } // thread safe. The int is copied onto the local stack.

static void MyFunction(Object o) { ... } // Not thread safe. Since o is a reference type, it might be shared among multiple threads. 

다른 팁

아니요, Addone은 여기에 스레드 안전합니다. 로컬 변수 만 사용합니다. 다음은 다음과 같습니다 그렇지 않을 것입니다 스레드 안전 :

 class BadCounter
 {
       private static int counter;

       public static int Increment()
       {
             int temp = counter;
             temp++;
             counter = temp;
             return counter;
       }
 }

여기서는 두 개의 스레드가 동시에 호출을 호출 할 수 있으며 한 번만 증가 할 수 있습니다. (사용 return ++counter; 그건 그렇고, 위의 것은 같은 것의 더 명백한 버전입니다. 더 분명히 틀릴 수 있도록 확장했습니다.)

스레드 - 안전이 무엇인지에 대한 세부 사항은 매우 까다로울 수 있지만 일반적으로 어떤 상태도 돌연변이를하지 않으면 (어쨌든 당신에게 전달 된 것 외에는 - 약간의 회색 영역)입니다. 대개 괜찮아.

스레딩 문제(최근에 걱정하고 있는 문제)는 별도의 캐시가 있는 다중 프로세서 코어 사용과 기본적인 스레드 교환 경쟁 조건에서 발생합니다.별도의 코어에 대한 캐시가 동일한 메모리 위치에 액세스하는 경우 일반적으로 다른 코어에 대해 전혀 모르고 해당 데이터 위치가 주 메모리(또는 전체 메모리에서 공유되는 동기화된 캐시)로 다시 돌아가지 않고도 해당 데이터 위치의 상태를 별도로 추적할 수 있습니다. 예를 들어 L2 또는 L3의 코어), 프로세서 성능상의 이유로.따라서 실행 순서 인터록 트릭도 다중 스레드 환경에서는 안정적이지 않을 수 있습니다.

아시다시피, 이 문제를 해결하는 주요 도구는 잠금입니다. 이는 독점 액세스(동일 잠금에 대한 경합 간)를 위한 메커니즘을 제공하고 기본 캐시 동기화를 처리하여 다양한 잠금 보호 장치를 통해 동일한 메모리 위치에 액세스할 수 있도록 합니다. 코드 섹션이 적절하게 직렬화됩니다.누가 언제 어떤 순서로 잠금을 받는지 사이에 경쟁 조건이 있을 수 있지만 일반적으로 잠긴 섹션의 실행이 원자성(해당 잠금의 컨텍스트 내에서)임을 보장할 수 있으면 처리하기가 훨씬 더 간단합니다.

모든 참조 유형의 인스턴스에 대해 잠금을 얻을 수 있습니다(예:int 또는 enum과 같은 값 유형이 아니라 null이 아닌 Object에서 상속됩니다. 그러나 객체에 대한 잠금은 해당 객체에 대한 액세스에 고유한 영향을 미치지 않으며 잠금을 설정하려는 다른 시도와만 상호 작용한다는 점을 이해하는 것이 매우 중요합니다. 같은 개체.적절한 잠금 체계를 사용하여 멤버 변수에 대한 액세스를 보호하는 것은 클래스에 달려 있습니다.때때로 인스턴스는 자신을 잠그어 자신의 멤버에 대한 멀티스레드 액세스를 보호할 수 있습니다(예: lock (this) { ... } ) 그러나 일반적으로 인스턴스는 한 명의 소유자만 보유하는 경향이 있고 인스턴스에 대한 스레드 안전 액세스를 보장할 필요가 없기 때문에 이는 필요하지 않습니다.

보다 일반적으로 클래스는 개인용 잠금을 생성합니다(예: private readonly object m_Lock = new Object(); 해당 인스턴스의 멤버에 대한 액세스를 보호하기 위해 각 인스턴스 내의 별도 잠금 private static readonly object s_Lock = new Object(); 클래스의 정적 멤버에 대한 액세스를 보호하기 위한 중앙 잠금 장치)Josh는 잠금 사용에 대한 보다 구체적인 코드 예제를 제공합니다.그런 다음 잠금을 적절하게 사용하도록 클래스를 코딩해야 합니다.더 복잡한 경우에는 함께 사용되지 않는 다양한 종류의 리소스에 대한 경합을 줄이기 위해 다양한 구성원 그룹에 대해 별도의 잠금을 생성할 수도 있습니다.

따라서 원래 질문으로 다시 돌아가면 자체 로컬 변수 및 매개변수에만 액세스하는 메서드는 스레드로부터 안전합니다. 왜냐하면 이러한 변수는 현재 스레드와 관련된 스택의 자체 메모리 위치에 존재하고 액세스할 수 없기 때문입니다. 다른 곳에서는 매개변수 인스턴스를 전달하기 전에 스레드 간에 공유하지 않는 한.

인스턴스 자체 멤버(정적 멤버 없음)와 매개변수 및 지역 변수에만 액세스하는 비정적 메서드는 단일 소유자가 사용하는 해당 인스턴스의 컨텍스트에서 잠금을 사용할 필요가 없습니다. 스레드로부터 안전해야 함). 하지만 인스턴스를 공유할 의도가 있고 스레드로부터 안전한 액세스를 보장하려는 경우 인스턴스는 해당 인스턴스에 특정한 하나 이상의 잠금을 사용하여 멤버 변수에 대한 액세스를 보호해야 합니다. 인스턴스 자체가 하나의 옵션임)-스레드로부터 안전한 공유가 가능하도록 의도되지 않은 항목을 공유할 때 자체 잠금을 구현하도록 호출자에게 맡기는 것과는 대조적입니다.

조작되지 않은 읽기 전용 멤버(정적 또는 비정적)에 대한 액세스는 일반적으로 안전하지만, 인스턴스가 보유한 인스턴스 자체가 스레드로부터 안전하지 않거나 여러 조작에서 원자성을 보장해야 하는 경우 다음이 필요할 수 있습니다. 자체 잠금 구성표를 사용하여 이에 대한 모든 액세스를 보호합니다.인스턴스가 자체적으로 잠금을 사용하는 경우 편리할 수 있는 경우입니다. 원자성을 위해 인스턴스에 대한 여러 액세스에 걸쳐 인스턴스에 대한 잠금을 간단히 얻을 수 있지만 단일 액세스에 대해서는 그렇게 할 필요가 없기 때문입니다. 자체적으로 잠금을 사용하여 해당 액세스를 개별적으로 스레드로부터 안전하게 만듭니다.(만약 여러분의 수업이 아니라면, 스스로 잠그는지 아니면 외부에서 접근할 수 없는 개인용 잠금 장치를 사용하고 있는지 알아야 합니다.)

마지막으로 인스턴스 내에서 정적 멤버(주어진 메서드 또는 다른 메서드에 의해 변경됨)를 변경하는 데 액세스할 수 있습니다. 물론 이러한 정적 멤버에 액세스하고 언제 어디서나 누구에게서나 호출할 수 있는 정적 메서드도 있습니다. 책임감 있는 잠금을 사용해야 하는 가장 큰 이유는 스레드로부터 안전하지 않고 예측할 수 없는 버그가 발생할 가능성이 높기 때문입니다.

.NET 프레임워크 클래스를 처리할 때 Microsoft는 특정 API 호출이 스레드로부터 안전한지 여부를 MSDN에 문서화합니다(예:다음과 같이 제공된 일반 컬렉션 유형의 정적 메서드 List<T> 인스턴스 메서드는 스레드로부터 안전하지만 그렇지 않을 수도 있습니다. 하지만 확실히 확인하려면 구체적으로 확인하세요.대부분의 경우(그리고 스레드로부터 안전하다고 구체적으로 언급하지 않는 한) 내부적으로는 스레드로부터 안전하지 않으므로 안전한 방식으로 사용하는 것은 사용자의 책임입니다.그리고 개별 작업이 스레드로부터 안전하게 내부적으로 구현되는 경우에도 원자성이 필요한 더 복잡한 작업을 수행하는 경우 코드에 의한 공유 및 중복 액세스에 대해 여전히 걱정해야 합니다.

한 가지 큰 경고는 컬렉션을 반복하는 것입니다(예:~와 함께 foreach).컬렉션에 대한 각 액세스가 안정적인 상태를 얻더라도 해당 액세스 사이에 변경되지 않을 것이라는 본질적인 보장은 없습니다(다른 곳에서 액세스할 수 있는 경우).컬렉션이 로컬에 보관되면 일반적으로 문제가 없지만 다른 스레드에 의해 또는 루프 실행 중에 변경될 수 있는 컬렉션은 일관성 없는 결과를 생성할 수 있습니다.이 문제를 해결하는 쉬운 방법 중 하나는 원자 스레드로부터 안전한 작업(보호 잠금 구성표 내부)을 사용하여 컬렉션의 임시 복사본을 만드는 것입니다(MyType[] mySnapshot = myCollection.ToArray();) 그런 다음 잠금 외부의 해당 로컬 스냅샷 복사본을 반복합니다.대부분의 경우 이렇게 하면 전체 시간 동안 잠금을 유지할 필요가 없지만 반복 내에서 수행하는 작업에 따라 충분하지 않을 수 있으며 전체 시간 변경으로부터 보호하면 됩니다(또는 이미 내부에 있을 수도 있음). 다른 것들과 함께 컬렉션을 변경하기 위한 접근을 방지하는 잠긴 섹션입니다.

따라서 스레드로부터 안전한 디자인에는 약간의 기술이 필요하며, 사물을 보호하기 위해 잠금을 얻을 수 있는 위치와 방법을 아는 것은 클래스의 전반적인 디자인과 사용법에 따라 크게 달라집니다.편집증에 빠지기 쉬우며 모든 것에 대해 모든 것을 잠궈야 한다고 생각하지만 실제로는 사물을 보호할 올바른 계층을 찾는 것이 중요합니다.

방법은 로컬 변수 만 사용하기 때문에 방법이 좋습니다. 방법을 조금 변경하겠습니다.

static int foo;

static int addOne(int someNumber)
{
  foo=someNumber; 
  return foo++;
}

정적 데이터를 터치하기 때문에 이것은 스레드 안전 방법이 아닙니다. 그런 다음 다음과 같이 수정해야합니다.

static int foo;
static object addOneLocker=new object();
static int addOne(int someNumber)
{
  int myCalc;
  lock(addOneLocker)
  {
     foo=someNumber; 
     myCalc= foo++;
  }
  return myCalc;
}

나는 이것이 바보 같은 샘플이라고 생각합니다. 내가 그것을 올바르게 읽으면 더 이상 foo에 아무런 의미가 없지만 샘플입니다.

비 스레드-안전 코드를 감지 할 수있는 연구가 있습니다. 예를 들어 프로젝트 Microsoft Research의 체스.

함수 외부의 변수를 수정하는 경우에만 레이스 조건 일뿐입니다. 당신의 예는 그렇게하지 않습니다.

그것은 기본적으로 당신이 찾고있는 것입니다. 스레드 안전은 기능을 의미합니다.

  1. 외부 데이터를 수정하지 않습니다
  2. 외부 데이터에 대한 액세스는 하나의 기능 만 한 번에 액세스 할 수 있도록 적절하게 동기화됩니다.

외부 데이터는 스토리지 (데이터베이스/파일) 또는 애플리케이션 내부 (변수, 클래스 인스턴스 등)에 보관 될 수 있습니다. 기본적으로 기능 범위를 벗어난 세계 어디에서나 선언 된 모든 것.

기능의 비 스레드 안전 버전의 사소한 예는 다음과 같습니다.

private int myVar = 0;

private void addOne(int someNumber)
{
   myVar += someNumber;
}

동기화없이 두 개의 다른 스레드에서 호출하면 AddOne에 대한 모든 호출이 완료된 후 쿼리가 발생하는지 여부에 따라 MyVar의 값이 다릅니다. 또는 두 통화 사이에서 쿼리가 발생하거나 쿼리가 발생하기 전에 쿼리가 발생합니다. 전화.

위의 예에서 번호.

스레드 안전은 주로 저장 상태와 관련이 있습니다. 위의 예제를 비 스레드를 안전하게 만들 수 있습니다.

static int myInt;

static int addOne(int someNumber){
myInt = someNumber;
return myInt +1; 
}

이것은 컨텍스트 스위치로 인해 스레드 1이 Call MyInt = sommenumber 및 컨텍스트 스위치로 이동할 수 있음을 의미합니다. 스레드 1이 5로 설정한다고 가정 해 봅시다. 그런 다음 스레드 2가 들어와 6을 사용하고 7을 반환한다고 상상해보십시오. 1 다시 깨어납니다. 5 대신 MyInt에 6 개가 사용되고 예상 대신 7을 반환합니다. 6 : o

어딘가에, 스레드 안전합니다 자원에 액세스 할 때 두 개 이상의 스레드가 충돌하지 않음을 의미합니다. 일반적으로 정적 변수 --- C#, vb.net 및 Java와 같은 언어로 --- 코드를 만들었습니다. 실 안전하지 않습니다.

자바에는 존재합니다 동기화 예어. 그러나 .NET에서는 어셈블리 옵션/지침을 얻습니다.


class Foo
{
    [MethodImpl(MethodImplOptions.Synchronized)]
    public void Bar(object obj)
    {
        // do something...
    }
}

비 스레드 안전 클래스의 예는 이루어져야합니다 싱글 톤,이 패턴은 어떻게 코딩되는지에 따라. 일반적으로 구현해야합니다 동기화 인스턴스 제작자.

당신이 원하지 않는다면 동기화 된 방법, 당신은 다음과 같은 잠금 방법을 시도 할 수 있습니다 스핀 잠금.

두 스레드가 동시에 사용할 수있는 순종에 대한 액세스는 ThreadSafe가 아닙니다.

파트 2의 예제는 인수로 전달 된 값 만 사용하므로 분명히 안전하지만 객체 스코프 변수를 사용하면 적절한 잠금 문으로 액세스를 둘러싸고 있어야합니다.

foo 동시 또는 순차적 호출간에 공유되지 않으므로 addOne 스레드 안전입니다.

예에서 'foo'와 'sommenumber'가 안전한 이유는 스택에 상주하고 각 스레드에 자체 스택이 있으므로 공유되지 않기 때문입니다.

예를 들어, 데이터가 공유 할 가능성이 있으면 전역이되거나 객체에 포인터를 공유 할 수있는 경우 충돌이 발생할 수 있으며 어떤 종류의 잠금 장치를 사용해야 할 수도 있습니다.

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