c# 레퍼런쓰 타잎 객체를 뜨레드의 아규먼트로 이용할 때 주의할 점

레퍼런쓰 타잎 객체를 뜨레드의 아규먼트로 이용하면 뜨레드에서 처리되는 동안 객체의 값이 바뀐 경우 뜨레드가 전달 받은 객체가 아닌 바뀐 객체가 뜨레드 안에서 이용되는 문제가 생긴다. 예를 들어 WndProc으로 윈도우즈의 특정 메씨지를 잡아서 이걸 아규먼트로 하여 뜨레드를 만들어 돌리는 경우를 생각해 볼 수 있다. 이베스트투자증권의 api를 이용하여 주식 등의 가격을 처리할 때가 이렇다. 삼성전자의 현재 가격을 받아서 복잡한 작업을 하기 위해 뜨레드를 만들어 보냈는데 이 작업이 끝나기 전에 윈도우즈가 삼성전자 현재 가격이 저장되어 있는 메모리에 주소에 카카오 현재 가격을 덮어써 버리면 삼성전자 현재 가격을 가지고 작동하던 뜨레드 안에서 삼성전자의 현재 가격이 카카오의 그것으로 바뀌어 버린다.

윈도우즈 메씨지는 레퍼런쓰 타잎의 객체다. 위와 같은 문제를 피하려면 레퍼런쓰 타잎 객체를 밸류 타잎으로 바꾼 뒤 뜨레드에 실어 보내면 된다. 위의 경우라면 Marshal.PtrToStructure를 이용하여 struct로 바꾸는 방법을 생각해 볼 수 있지만 쉽진 않다. 써버가 보내는 구조체 안에 포인터가 들어 있기 때문이다. 아래의 예제는 밸류 타잎인 struct의 새로운 인스턴쓰를 만들어도 그 안에 레퍼런쓰 타잎의 필드가 있다면 이건 여전히 기존의 것과 단절되지 않고 있다는 걸 보여 준다.

struct Struct1
{
    public List<int> Ints;
}

Struct1 Struct1_1 = new();

private void Form1_Load(object sender, EventArgs e)
{
    Struct1_1.Ints = new();

    Struct1_1.Ints.Add(1);

    Text = Struct1_1.Ints[0].ToString();
}

private void button1_Click(object sender, EventArgs e)
{
    Struct1 struct1 = Struct1_1;

    Struct1_1.Ints[0] = 2;

    button1.Text = struct1.Ints[0].ToString(); // 2
}

예를 들어 클래쓰를 뜨레드의 아규먼트로 써야 한다면 클래쓰를 스트럭트로 바꾸는 것만으로는 충분하지 않고 클래쓰 안에 레퍼런스 타잎 객체가 있다면 이거까지 밸류 타잎으로 바꿔야 제대로 작동한다.