programing

.NET에 좀비가 존재합니까?

telecom 2023. 5. 15. 21:17
반응형

.NET에 좀비가 존재합니까?

저는 동료와 감금에 대해 논의하고 있었습니다.NET. 그는 저급 프로그래밍과 고급 프로그래밍 모두에서 폭넓은 경험을 가진 정말 똑똑한 사람입니다. 하지만 저급 프로그래밍에 대한 그의 경험은 저를 훨씬 능가합니다.어쨌든, 그는 그렇게 주장했습니다.NET 잠금은 "좀비 스레드"가 시스템을 충돌시킬 가능성이 거의 없는 것을 방지하기 위해 가능한 한 많은 부하가 걸릴 것으로 예상되는 중요한 시스템에서는 피해야 합니다.저는 락킹을 일상적으로 사용하고 "좀비 스레드"가 무엇인지 몰라서 물었습니다.제가 그의 설명에서 얻은 인상은 좀비 스레드는 종료되었지만 여전히 일부 리소스를 보유하고 있다는 것입니다.그가 제시한 좀비 스레드가 시스템을 파괴할 수 있는 예는 스레드가 어떤 객체를 잠그고 나서 일부 절차를 시작한 다음 잠금을 해제하기 전에 종료된다는 것입니다.이 경우 시스템이 손상될 수 있습니다. 이 메서드를 실행하려고 하면 잠긴 개체를 사용하는 스레드가 비활성 상태이기 때문에 스레드가 모두 반환되지 않는 개체에 대한 액세스를 대기하게 됩니다.

요점을 알 것 같습니다만, 제가 베이스를 벗어나면 알려주세요.그 개념은 저에게 이해가 되었습니다.저는 이것이 에서 일어날 수 있는 실제 시나리오라고 완전히 확신하지 못했습니다.NET. "좀비"에 대해서는 들어본 적이 없지만, 낮은 수준에서 심층적으로 작업한 프로그래머들은 (스레드와 같은) 컴퓨팅 기본 원리에 대해 더 깊이 이해하는 경향이 있다는 것을 알고 있습니다.하지만 저는 잠금의 가치를 확실히 알고 있으며, 많은 세계적인 프로그래머들이 잠금을 활용하는 것을 보았습니다.저는 또한 이것을 스스로 평가할 수 있는 능력이 제한되어 있습니다. 왜냐하면 저는 그것을 알고 있기 때문입니다.lock(obj)로 단지 진은정말단다위음을통설한탕다사니입적지술▁for:.

bool lockWasTaken = false;
var temp = obj;
try { Monitor.Enter(temp, ref lockWasTaken); { body } }
finally { if (lockWasTaken) Monitor.Exit(temp); }

그리고 그 이유는Monitor.Enter그리고.Monitor.Exit표시되어 있습니다.extern그것은 생각할 수 있는 것처럼 보입니다.NET은 이런 종류의 영향을 미칠 수 있는 시스템 구성 요소에 노출되지 않도록 스레드를 보호하는 일종의 처리를 수행하지만, 이는 순전히 추측에 불과하며 "좀비 스레드"에 대해 들어본 적이 없다는 사실에 근거한 것입니다.여기서 이에 대한 피드백을 받을 수 있기를 바랍니다.

  1. 여기서 설명한 것보다 "좀비 스레드"에 대한 더 명확한 정의가 있습니까?
  2. 에서 좀비 스레드가 발생할 수 있습니다.NET? (왜/왜 안됩니까?)
  3. 해당되는 경우 어떻게 에서 좀비 스레드를 강제로 생성할 수 있습니까?NET?
  4. 해당되는 경우 에서 좀비 스레드 시나리오를 위험에 빠뜨리지 않고 잠금을 활용하려면 어떻게 해야 합니까?NET?

갱신하다

저는 2년 조금 전에 이 질문을 했습니다.오늘 이런 일이 있었습니다.

개체가 좀비 상태에 있습니다.

  • 여기서 설명한 것보다 "좀비 스레드"에 대한 더 명확한 정의가 있습니까?

종료되었지만(따라서 더 이상 리소스를 방출할 수 없음) 리소스(예: 핸들)가 여전히 주변에 있고 (잠재적으로) 문제를 일으키는 스레드인 것 같습니다.

  • 에서 좀비 스레드가 발생할 수 있습니다.NET? (왜/왜 안됩니까?)
  • 해당되는 경우 어떻게 에서 좀비 스레드를 강제로 생성할 수 있습니까?NET?

그들은 정말 그래요, 보세요, 제가 만들었어요!

[DllImport("kernel32.dll")]
private static extern void ExitThread(uint dwExitCode);

static void Main(string[] args)
{
    new Thread(Target).Start();
    Console.ReadLine();
}

private static void Target()
{
    using (var file = File.Open("test.txt", FileMode.OpenOrCreate))
    {
        ExitThread(0);
    }
}

" 프로그램스은시다니작합이를레드▁a" 시작합니다.Target파일을 연 다음 을 사용하여 즉시 스스로 목숨을 끊습니다. 결과 좀비 스레드는 핸들을 "test.txt" 파일로 해제하지 않으므로 프로그램이 종료될 때까지 파일이 열린 상태로 유지됩니다(프로세스 탐색기 등에 확인할 수 있습니다). "test.txt"에 대한 핸들은 다음 시간까지 릴리스되지 않습니다.GC.Collect라고 불립니다 - 알고 보니 손잡이가 새는 좀비 스레드를 만드는 것은 제가 생각했던 것보다 훨씬 더 어려운 것입니다.

  • 해당되는 경우 에서 좀비 스레드 시나리오를 위험에 빠뜨리지 않고 잠금을 활용하려면 어떻게 해야 합니까?NET?

내가 방금 한 것처럼 하지 마!

코드가 자체적으로 올바르게 정리되는 한(관리되지 않는 리소스로 작업하는 경우 안전 핸들 또는 동등한 클래스 사용), 그리고 이상하고 멋진 방법으로 스레드를 죽이지 않는 한(가장 안전한 방법은 스레드를 절대로 죽이지 않는 것입니다. 필요한 경우에는 예외를 통해 종료하도록 합니다.),좀비 스레드와 유사한 것을 가질 수 있는 유일한 방법은 무언가 매우 잘못되었을 때입니다(예: CLR에서 무언가 잘못되었을 때).

사실 좀비 스레드를 만드는 것은 사실 놀라울 정도로 어렵습니다. (문서에서 C 외부로 호출하지 말라는 기능을 P/Invoke해야 했습니다.)예를 들어, 다음의 (끔찍한) 코드는 실제로 좀비 스레드를 만들지 않습니다.

static void Main(string[] args)
{
    var thread = new Thread(Target);
    thread.Start();
    // Ugh, never call Abort...
    thread.Abort();
    Console.ReadLine();
}

private static void Target()
{
    // Ouch, open file which isn't closed...
    var file = File.Open("test.txt", FileMode.OpenOrCreate);
    while (true)
    {
        Thread.Sleep(1);
    }
    GC.KeepAlive(file);
}

실수를 , 닫힙니다.Abort를 호출합니다(최종 사용자의 일부로 사용).fileSafeFileHandle을 사용하여 파일 핸들을 감쌀 수 있습니다.)

C의 잠금 예제.스레드가 이상하지 않은 방식으로 종료될 때 리소스(이 경우 잠금)를 해제하지 못하는 가장 쉬운 방법은 huis의 답변일 수도 있지만 두 가지 방법 중 하나를 사용하여 쉽게 해결할 수 있습니다.lock를 신대진술, 또릴를 에 .finally블록으로 막다

참고 항목

답변을 조금 정리했지만, 아래 원본은 참조용으로 남겨두었습니다.

좀비라는 용어는 처음 들어보는데, 그 정의는 다음과 같습니다.

모든 리소스를 해제하지 않고 종료된 스레드

이 정의를 고려할 때, 예, 에서 이를 수행할 수 있습니다.다른 언어(C/C++, Java)와 마찬가지로 NET.

하지만, 저는 이것이 스레드화된 미션 크리티컬 코드를 작성하지 않는 좋은 이유라고 생각하지 않습니다.NET. 반대하는 다른 이유가 있을 수 있습니다.NET 그러나 쓰기.네가 좀비 스레드를 가질 수 있다고 해서 나는 NET을 이해할 수 없습니다.좀비 스레드는 C/C++에서 가능하며(C에서는 훨씬 더 쉽게 망친다고 주장할 수도 있습니다), 많은 중요한 스레드 앱이 C/C++(대량 거래, 데이터베이스 등)에 있습니다.

결론적으로 사용할 언어를 결정하는 중이라면 성능, 팀 기술, 일정, 기존 앱과의 통합 등 전체적인 그림을 고려하는 것이 좋습니다.물론, 좀비 스레드는 여러분이 생각해야 할 것이지만, 실제로 이 실수를 하는 것은 매우 어렵기 때문입니다.NET은 C와 같은 다른 언어들과 비교했을 때 위에서 언급한 것들과 같은 다른 것들에 의해 이러한 우려가 가려질 것이라고 생각합니다.행운을 빕니다.

올바른 스레드화 코드를 작성하지 않으면 Original Answer 좀비가 존재할 수 있습니다.C/C++ 및 Java와 같은 다른 언어도 마찬가지입니다.하지만 이것이 스레드 코드를 에 쓰지 않는 이유는 아닙니다.그물.

그리고 다른 언어와 마찬가지로, 무언가를 사용하기 전에 가격을 알아야 합니다.또한 잠재적인 문제를 예측할 수 있도록 후드 아래에서 무슨 일이 일어나고 있는지 알 수 있습니다.

미션 크리티컬 시스템을 위한 신뢰할 수 있는 코드는 어떤 언어를 사용하든 쉽게 작성할 수 없습니다.하지만 저는 에서 정확하게 하는 것이 불가능하지 않다고 확신합니다.NET. 또한 AFAIK, .NET 스레드는 C/C++의 스레드와 크게 다르지 않으며, 일부 .net 특정 구조체(RWL 및 이벤트 클래스의 경량 버전과 같은)를 제외하고 동일한 시스템 호출을 사용합니다.

좀비라는 용어를 처음 들어봤지만, 당신의 설명에 따르면, 당신의 동료는 아마도 모든 자원을 방출하지 않고 종료된 스레드를 의미했을 것입니다.이로 인해 잠재적으로 교착 상태, 메모리 누수 또는 기타 부작용이 발생할 수 있습니다.이것은 분명히 바람직하지 않지만 유일한 것입니다.이러한 가능성 때문에 NET은 다른 언어로도 가능하기 때문에 아마도 좋은 생각이 아닐 것입니다.저는 심지어 C/C++보다 C/C++에서 망치는 것이 더 쉽다고 주장합니다.NET(특히 RAII가 없는 C의 경우) 하지만 많은 중요한 앱이 C/C++로 작성되어 있죠?그래서 그것은 정말로 여러분의 개인적인 상황에 달려있습니다.응용 프로그램에서 모든 속도를 추출하고 가능한 한 베어 메탈에 근접하고 싶다면 다음과 같이 하십시오.NET은 최선의 해결책이 아닐 수도 있습니다.예산이 부족하고 웹 서비스/기존 .net 라이브러리 등과 많은 인터페이스를 수행하는 경우.NET이 좋은 선택일 수 있습니다.

현재 제 답변의 대부분은 아래의 코멘트로 수정되었습니다.답변을 삭제하지 않습니다. 평판 점수가 필요하기 때문입니다. 왜냐하면 댓글에 있는 정보는 독자들에게 가치가 있을 수 있기 때문입니다.

Important Blue는 에서 이를 지적했습니다. 2 NET 2.0 이상 »finally블록은 스레드 중단의 영향을 받지 않습니다.Andreas Niedermair가 언급했듯이, 이것은 실제 좀비 스레드가 아닐 수도 있지만, 다음 예제는 스레드를 중단하는 것이 어떻게 문제를 일으킬 수 있는지 보여줍니다.

class Program
{
    static readonly object _lock = new object();

    static void Main(string[] args)
    {
        Thread thread = new Thread(new ThreadStart(Zombie));
        thread.Start();
        Thread.Sleep(500);
        thread.Abort();

        Monitor.Enter(_lock);
        Console.WriteLine("Main entered");
        Console.ReadKey();
    }

    static void Zombie()
    {
        Monitor.Enter(_lock);
        Console.WriteLine("Zombie entered");
        Thread.Sleep(1000);
        Monitor.Exit(_lock);
        Console.WriteLine("Zombie exited");
    }
}

그나사경우할을 lock() { } 록블, 더finally다음과 같은 경우에도 여전히 실행됩니다.ThreadAbortException그런 식으로 발사됩니다.

다음 정보는 에 대해서만 유효합니다.NET 1 및 .NET 1.1:

안에 있는 lock() { }하면 차단합니다.ThreadAbortException정확하게 도착할 때.finally블록을 실행하려고 합니다. 잠금이 해제되지 않았습니다.말씀하신 것처럼,lock() { }블록은 다음과 같이 컴파일됩니다.

finally 
{
    if (lockWasTaken) 
        Monitor.Exit(temp); 
}

다른 스레드가 호출하는 경우Thread.Abort()된 성된 finally블록, 잠금이 해제되지 않을 수 있습니다.

이것은 좀비 스레드에 관한 것이 아니지만, 효과적인 C# 책에는 ID 일회용 구현 섹션(항목 17)이 있는데, 이 섹션은 여러분이 흥미롭게 생각할 수 있는 좀비 객체에 대해 이야기합니다.

저는 책 자체를 읽는 것을 추천합니다. 하지만 요점은 수업이 ID 일회용을 구현하거나 디스트럭터를 포함하는 경우, 여러분이 해야 할 일은 자원을 방출하는 것입니다.여기서 다른 작업을 수행하면 개체가 가비지 수집되지 않을 뿐만 아니라 어떤 방식으로든 액세스할 수 없을 가능성이 있습니다.

아래와 유사한 예를 제시합니다.

internal class Zombie
{
    private static readonly List<Zombie> _undead = new List<Zombie>();

    ~Zombie()
    {
        _undead.Add(this);
    }
}

이 개체의 소멸자가 호출되면 자신에 대한 참조가 전역 목록에 추가됩니다. 즉, 프로그램이 실행되는 동안에는 해당 개체가 활성 상태로 메모리에 남아 있지만 액세스할 수 없습니다.이는 리소스(특히 관리되지 않는 리소스)가 완전히 릴리스되지 않을 수 있으므로 모든 종류의 잠재적인 문제가 발생할 수 있음을 의미할 수 있습니다.

더 완전한 예는 다음과 같습니다.각 루프에 도달할 때까지 Undead 목록에 각각 이미지가 포함된 150개의 개체가 있지만 이미지가 GC로 지정되어 있으므로 사용하려고 하면 예외가 발생합니다.이 예에서는 인수가 표시됩니다.예외(파라미터가 유효하지 않음)는 이미지를 저장하거나 높이 및 너비와 같은 치수를 보려고 할 때도 마찬가지입니다.

class Program
{
    static void Main(string[] args)
    {
        for (var i = 0; i < 150; i++)
        {
            CreateImage();
        }

        GC.Collect();

        //Something to do while the GC runs
        FindPrimeNumber(1000000);

        foreach (var zombie in Zombie.Undead)
        {
            //object is still accessable, image isn't
            zombie.Image.Save(@"C:\temp\x.png");
        }

        Console.ReadLine();
    }

    //Borrowed from here
    //http://stackoverflow.com/a/13001749/969613
    public static long FindPrimeNumber(int n)
    {
        int count = 0;
        long a = 2;
        while (count < n)
        {
            long b = 2;
            int prime = 1;// to check if found a prime
            while (b * b <= a)
            {
                if (a % b == 0)
                {
                    prime = 0;
                    break;
                }
                b++;
            }
            if (prime > 0)
                count++;
            a++;
        }
        return (--a);
    }

    private static void CreateImage()
    {
        var zombie = new Zombie(new Bitmap(@"C:\temp\a.png"));
        zombie.Image.Save(@"C:\temp\b.png");
    }
}

internal class Zombie
{
    public static readonly List<Zombie> Undead = new List<Zombie>();

    public Zombie(Image image)
    {
        Image = image;
    }

    public Image Image { get; private set; }

    ~Zombie()
    {
        Undead.Add(this);
    }
}

다시 말씀드리지만, 좀비 스레드에 대해 특별히 질문하신 것은 알지만, 질문 제목은 .net에 있는 좀비에 대한 것이고, 저는 이것을 상기했고 다른 사람들이 흥미롭게 여길지도 모른다고 생각했습니다!

부하가 큰 중요한 시스템에서는 주로 성능이 향상되었기 때문에 잠금이 없는 코드를 작성하는 것이 좋습니다.LMAX와 같은 것들과 이것에 대한 훌륭한 논의를 위해 어떻게 "기계적 공감"을 활용하는지 보세요.그래도 좀비 실을 걱정합니까?저는 그것이 해결해야 할 버그일 뿐 사용하지 않을 만큼 충분한 이유가 아닌 에지 케이스라고 생각합니다.lock.

당신의 친구가 그저 화려하고 알려지지 않은 이국적인 용어에 대한 지식을 나에게 과시하는 것처럼 들립니다!Microsoft UK에서 성능 연구소를 운영하는 동안 에서 이 문제의 사례를 본 적이 없습니다.그물.

1. 여기서 설명한 것보다 "좀비 스레드"에 대한 더 명확한 정의가 있습니까?

저는 "좀비 스레드"가 존재한다는 것에 동의합니다. 이 용어는 놓지 않는 자원이 남아 있지만 완전히 죽지 않는 스레드에서 일어나는 일을 가리키는 용어입니다. 따라서 "좀비"라는 이름이 있습니다. 따라서 이 참조에 대한 당신의 설명은 돈에 꽤 맞습니다!

2. 좀비 스레드가 발생할 수 있습니다.NET? (왜/왜 안됩니까?)

예, 발생할 수 있습니다.이는 참조이며 실제로 Windows에서 "좀비"라고 부릅니다. MSDN은 데드 프로세스/스레드에 대해 "좀비"라는 단어를 사용합니다.

흔히 일어나는 일은 다른 이야기이며, 스레드 록킹을 좋아하고 한동안 그것을 해왔던 당신의 경우, 저는 당신에게 일어나는 시나리오에 대해 걱정조차 하지 않을 것입니다.

그리고 예, @KevinPanko가 코멘트에서 정확하게 언급했듯이, "좀비 스레드"는 유닉스에서 나온 것입니다. 이것이 XCode-ObjectiveC에서 사용되고 "NS좀비"라고 불리며 디버깅에 사용되는 이유입니다.거의 같은 방식으로 행동합니다.유일한 차이점은 죽은 개체가 디버깅을 위해 "좀비 스레드" 대신 "좀비 개체"가 된다는 것입니다. 이는 코드에 잠재적인 문제가 될 수 있습니다.

저는 좀비 실을 충분히 쉽게 만들 수 있습니다.

var zombies = new List<Thread>();
while(true)
{
    var th = new Thread(()=>{});
    th.Start();
    zombies.Add(th);
}

경우 스레드이 누출됩니다(의 경우).Join() 관리되는 세계에서 우리가 생각하는 또 다른 메모리 유출일 뿐입니다.

그렇다면, 실제로 자물쇠를 고정하는 방식으로 실을 죽이는 것은 후방의 고통이지만 가능합니다.다른남의자.ExitThread()그 일을 합니다.발견한 와 같이 파일 에 의해 되었지만, "file ", " handle", "file handle", "file handle",lock물체가 하지 않을 주위에.그런데 왜 그런 짓을 하는 겁니까?

언급URL : https://stackoverflow.com/questions/20065780/do-zombies-exist-in-net

반응형