C#/Problems

멀티스레딩 + SetCursorPosition에서 생긴 문제 [해결]

DueCare 2019. 4. 1. 17:37

구상은 (x,y)좌표를 기반으로 (0,0)에서는 출력하는 스레드가 활동을 하고,

(0,20)에서는 입력받는 스레드가 활동을 하는 것을 구상했다.


(0,0)에서 메뉴를 출력한 후에 (0,20)에서 입력받고 다시 (0,0)으로 올라가서 그에 대한 답을 출력하는 방식으로 코딩을 했는데

자꾸 위의 사진처럼 (0,0)이 끝난 뒤에 (0,20)으로 내려가지 않고 저 자리에서 입력을 1회 해야했다.

이처럼 aaaaa의 좌표에서 1회 입력을 해야만 ddddd가 있는 (0,20)좌표로 커서가 이동했다.

무엇이 문제일까 계속 만져보고 SetCursorPosition을 따로 메서드에 지정하여 lock도 걸어보았지만 똑같았다.

또, 이렇게 멋대로 커서가 왔다갔다하면서 출력이 늘어질 때도 있었다.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
        public void SetCursor(int x, int y) //커서 좌표 지정 lock하여 한번에 1스레드만 이용가능
        {
            lock(lockObject)
            {
                Console.SetCursorPosition(x, y);
            }
        }
        public void StartOutputThread() //아웃풋 스레드 생성 및 실행
        {
            this.SetCursor(00);
            ThreadStart outputThreadStart = new ThreadStart(this.MenuOutput);
            Thread outputThread = new Thread(outputThreadStart);
            outputThread.Start();
        }
        public void StartInputThread() //인풋 스레드 생성 및 실행
        {
            this.SetCursor(020);
            ThreadStart inputThreadStart = new ThreadStart(this.Input);
            Thread inputThread = new Thread(inputThreadStart);
            inputThread.Start();
        }
cs

(스레드와 직접적으로 관련된 메서드 부분)


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void SetCursor(int x, int y) //커서 좌표 지정 lock하여 한번에 1스레드만 이용가능
        {
            lock(lockObject)
            {
                Console.SetCursorPosition(x, y);
            }
        }
        public void StartOutputThread() //아웃풋 스레드 생성 및 실행
        {
            this.SetCursor(00);
            ThreadStart outputThreadStart = new ThreadStart(this.MenuOutput);
            Thread outputThread = new Thread(outputThreadStart);
            outputThread.Priority = ThreadPriority.Highest;
            outputThread.Start();
        }
        public void StartInputThread() //인풋 스레드 생성 및 실행
        {
            this.SetCursor(020);
            ThreadStart inputThreadStart = new ThreadStart(this.Input);
            Thread inputThread = new Thread(inputThreadStart);
            inputThread.Priority = ThreadPriority.Lowest;
            inputThread.Start();
        }
 
cs

아웃풋 스레드와 인풋 스레드 간의 우선순위때문에 그러는 것 같아서

아웃풋 스레드에게 최우선순위를 주고, 인풋 스레드에게 최하 우선순위를 주었지만, 똑같은 현상이 반복되었다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        public void SetCursor(int x, int y) //커서 좌표 지정 lock하여 한번에 1스레드만 이용가능
        {
            lock(lockObject)
            {
                Console.SetCursorPosition(x, y);
            }
        }
        public void StartOutputThread() //아웃풋 스레드 생성 및 실행
        {
            this.SetCursor(00);
            ThreadStart outputThreadStart = new ThreadStart(this.MenuOutput);
            Thread outputThread = new Thread(outputThreadStart);
            outputThread.Start();
            this.StartInputThread();
        }
        public void StartInputThread() //인풋 스레드 생성 및 실행
        {
            this.SetCursor(020);
            ThreadStart inputThreadStart = new ThreadStart(this.Input);
            Thread inputThread = new Thread(inputThreadStart);
            inputThread.Start();
        }
 
cs
호출을 연쇄적으로 하게끔 아웃풋스레드 메서드에서 인풋스레드 메서드를 호출해봤으나 똑같았다.

키포인트는 제어권인거 같은데 풀 수가 없었다.

그렇게 이것저것 해보고 조언도 구해서 결국 답을 찾았다.

제어권 문제가 정답이었고, 그의 해답은 async, await에 있었다.

해결을 위해 async, await을 공부하는 중에 "그렇다면 스레드가 활동 중일 때 안 놓으면 되지않나?" 싶어서 Thread.Join();을 사용해봤다


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        public void SetCursor(int x, int y) //커서 좌표 지정 lock하여 한번에 1스레드만 이용가능
        {
            lock(lockObject)
            {
                Console.SetCursorPosition(x, y);
            }
        }
        public async void StartOutputThread() //아웃풋 스레드 생성 및 실행
        {
            this.SetCursor(00);
            ThreadStart outputThreadStart = new ThreadStart(this.MenuOutput);
            Thread outputThread = new Thread(outputThreadStart);
            outputThread.Start();
            outputThread.Join();
        }
        public async void StartInputThread() //인풋 스레드 생성 및 실행
        {
            this.SetCursor(020);
            ThreadStart inputThreadStart = new ThreadStart(this.Input);
            Thread inputThread = new Thread(inputThreadStart);
            inputThread.Start();
            inputThread.Join();
        }
cs

바로 outputThread.Join(); inputThread.Join(); 부분이다.

Thread의 Join 메서드는 사용한 스레드의 작업이 완료될때까지 호출자를 차단한다.

즉 App.cs에서 StartOuputThread() 메서드를 호출했는데, 원래는 outputThread.Start();가 실행되고 작업이 완료되기 전에 App.cs로 돌아가 StartInputThread(); 메서드를 호출하여 인풋스레드를 실행하는 것이다.   

하지만 위처럼 아웃풋 스레드를 실행하고, Join메서드를 사용해주면 작업이 완료되기 전에 호출자가 제어권을 가져갈 수 없다.

그렇게 해결을 했다.



이제 이 코드에 async, await, task를 사용해서 구현해봐야겠다.




-async await 공부중... 공부하고 코드 적용 후 업데이트 할 예정-