2/04/2010

Replace Thread.Suspend() and Resume() method

Replace Thread.Suspend() and Resume() method
Thread Suspend() Resume() 메소드 대체하기

RiskNET Packet analysis 프로그램을 개발 중 데이터가 20만줄이 넘어가서 한번에 불러와 처리하기에는

시간이 너무 오래 걸리는 관계로 쓰레드를 사용하게 되었다.

간단하게 textbox에 있는 20만줄의 글을 ListView에 뿌려주는 일이었는데

thread 클래스를 사용중에 suspend() 와 resume() 메소드를 사용했더니 '경고'가 뜨는것이 아닌가!?

이유는 더이상 사용하지 않는 Method... 바로  Obsolete Attribute 때문이었다.

http://msdn.microsoft.com/ko-kr/library/system.threading.thread.suspend%28VS.80%29.aspx
http://msdn.microsoft.com/ko-kr/library/system.threading.thread.resume%28VS.80%29.aspx

비록 실행은 되지만 찜찜한 기분에 이리저리 구글링을 해 보니 다음 아티클을 찾을 수 있었다.

아티클 바로가기

위 아티클에서 핵심은 아래 두 소스와 같다.

private ManualResetEvent suspendChangedEvent = new ManualResetEvent(false);
private ManualResetEvent terminateEvent = new ManualResetEvent(false);
ManualResetEvent Class는 로컬에서 신호를 보내 Event Handle을 할 수 있도록 도와준다.
이걸로 신호가 Set 되면 해당 Thread를 종료하거나 중지 또는 재시작하는 로직을 구현할 수 있게 해준다.

suspendChangedEvent.WaitOne(0, true);
terminateEvent.WaitOne(0, true);
WaitHandle.WaitAny(new WaitHandle[] { suspendChangedEvent, terminateEvent })
WaitOne method는 해당 변수가 Set 되어있으면 true를 Reset 되어있으면 false를 반환한다.
WaitHandle.WaitAny 는 주어진 매개변수중 하나라도 신호를 받을 때까지 대기한다.


아래 코드 전문은 위의 아티클에서 가져와 주석을 첨가한것이다.


소스코드
SuspendableThread.cs
namespace ThreadTest
{
    abstract public class SuspendableThread
    {
        #region Data
        private ManualResetEvent suspendChangedEvent = new ManualResetEvent(false);
        private ManualResetEvent terminateEvent = new ManualResetEvent(false);
        private long suspended;
        private Thread thread;
        private System.Threading.ThreadState failsafeThreadState = System.Threading.ThreadState.Unstarted;
        #endregion Data

        public SuspendableThread() { }

        private void ThreadEntry()
        {
            failsafeThreadState = System.Threading.ThreadState.Stopped;
            OnDoWork();
        }

        //자식클래스에서 구현할 부분 (Thread에서 돌아가는 부분)
        protected abstract void OnDoWork();

        protected Boolean SuspendIfNeeded()
        {
            Boolean suspendEventChanged = suspendChangedEvent.WaitOne(0, true);
            
            //suspendChangeEvent가 Set 되어있는 경우 실행
            if (suspendEventChanged)
            {
                Boolean needToSuspend = Interlocked.Read(ref suspended) != 0;
                suspendChangedEvent.Reset();

                //suspended 가 0이 아닌경우에만 실행
                //Suspend method실행시 suspended = 1, Resume method 실행시 suspended = 0
                if (needToSuspend)
                {
                    /// Suspending...
                    /// WaitHandle.WaitAny method: 
                    ///   주어진 매개변수들중 하나라도 신호를 받으면 신호를 받은 매개변수의 인덱스 반환
                    if (1 == WaitHandle.WaitAny(new WaitHandle[] { suspendChangedEvent, terminateEvent }))
                    {
                        //terminateEvent 변수가 신호를 받으면 true 반환
                        return true;
                    }
                    /// suspendChagedEvent 변수가 신호를 받으면 여기부터 실행
                    /// 이러한 이유로 Suspend method를 실행하면 OnDoWork method의 루프가 한번 더 실행됨
                    /// ...Waking
                }
            }
            return false;
        }

        protected bool HasTerminateRequest()
        {
            //terminateEvent가 Set이 되었으면 true 반환 Set이 안되어있으면 false 반환
            return terminateEvent.WaitOne(0, true);
        }

        public void Start()
        {
            thread = new Thread(new ThreadStart(ThreadEntry));

            // make sure this thread won't be automaticaly
            // terminated by the runtime when the
            // application exits
            thread.IsBackground = false;

            thread.Start();
        }

        public void Join()
        {
            if (thread != null)
            {
                thread.Join();
            }
        }

        public Boolean Join(Int32 milliseconds)
        {
            if (thread != null)
            {
                return thread.Join(milliseconds);
            }
            return true;
        }

        /// Not supported in .NET Compact Framework
        public Boolean Join(TimeSpan timeSpan)
        {
            if (thread != null)
            {
                return thread.Join(timeSpan);
            }
            return true;
        }

        public void Terminate()
        {
            terminateEvent.Set();
        }
        
        public void TerminateAndWait()
        {
            terminateEvent.Set();
            thread.Join();
        }

        public void Suspend()
        {
            //suspended를 1로 바꿔줌
            while (1 != Interlocked.Exchange(ref suspended, 1)) { }
            suspendChangedEvent.Set();
        }

        public void Resume()
        {
            while (0 != Interlocked.Exchange(ref suspended, 0)) { }
            suspendChangedEvent.Set();
        }

        public System.Threading.ThreadState ThreadState
        {
            get
            {
                if (null != thread)
                {
                    return thread.ThreadState;
                }
                return failsafeThreadState;
            }
        }
    }
}


SuspendableThreadWork.cs
namespace ThreadTest
{
    public class SuspendableThreadWork : SuspendableThread
    {
        public SuspendableThreadWork()
        {
        }

        protected override void OnDoWork()
        {
            try
            {
                while (false == HasTerminateRequest())
                {
                    Boolean awokenByTerminate = SuspendIfNeeded();
                    
                    if (awokenByTerminate)
                    {
                        return;
                    }
                    // TODO: replace the following to lines
                    // 코드작성
                }
            }

            finally
            {
                // TODO: Replace the following line with thread
                // exit processing.
                // 코드작성
            }
        }
    }
}


참고자료:
본문 아티클: http://msmvps.com/blogs/peterritchie/archive/2006/10/13/_2700_System.Threading.Thread.Suspend_280029002700_-is-obsolete_3A00_-_2700_Thread.Suspend-has-been-deprecated_2E002E002E00_.aspx
WaitHandle.WaitAny Method: http://msdn.microsoft.com/ko-kr/library/tdykks7z%28VS.80%29.aspx
ManualResetEvent Class: http://msdn.microsoft.com/ko-kr/library/system.threading.manualresetevent%28VS.95%29.aspx

댓글 없음:

댓글 쓰기