2016년 12월 29일 목요일

C# BeginInvoke 올바른 사용법

BeginInvoke 호출할때 보통 람다식을 사용해서 아래와 같이 쓴다

        private void SetPictureImage(PictureBox picture, Bitmap bitmap)
        {
            Action action = () =>
            {
                if (picture.Image != null) picture.Image.Dispose();
                picture.Image = bitmap;              
            };
            if (this.InvokeRequired) BeginInvoke(action);
            else action();
        }

위 코드의 문제점은 action 이 수행되는 동안 SetPictureImage 함수가 외부 쓰레드에서 호출될때 사용중인 picture, bitmap 변수가 변경될 수 있다는 점이다.

이게 무슨 말이냐면 action 함수 내의 코드와 바깥의 SetPictureImage 함수가 서로 다른 쓰레드에서 호출될 수 있다는 말이다. (InvokeRequired = true 인 경우)
이럴경우 action 함수에서 picture,bitmap 변수를 사용하는 도중 SetPictureImage 함수가 호출되면 picture,bitmap 변수값이 사용도중 바뀌어 에러를 발생시킨다.

이것을 방지하기위해 아래와 같이 action 호출시 변수들을 매개변수로 넘겨주면 버그를 방지할 수 있다.

        private void SetPictureImage(PictureBox picture, Bitmap bitmap)
        {
            Action<PictureBox, Bitmap> action = (picture1, bitmap1) =>
                {
                    if (picture1.Image != null) picture1.Image.Dispose();
                    picture1.Image = bitmap1;
                };
            if (this.InvokeRequired) BeginInvoke(action, picture, bitmap);
            else action(picture, bitmap);
        }