-
[Async/Await] Invoke보다 간편하게 UI 접근하기.NET/개념 및 유용한 팁 2019. 12. 19. 23:46반응형
비동기 프로그래밍(async, await)에 대한 올바른 사용법에 대한 문서가 있습니다.
https://ddochea.tistory.com/212 를 참조해주세요.2015년도에 작성했던 포스트 "2015/01/11 - [C#] - [Invoke & BeginInvoke] 1. 다른 Thread 에서 UI 접근하기 (1)" 가 현재까지도 부동의 1순위를 기록하고 있다. 일간 방문자수의 절반이상이 해당 포스트 관련 접근이다.
멱살잡고 캐리중그런데 좀 찝찝하다. 2012년쯤? 부터 .NET 4.5 및 C# 5.0가 발표되면서 async, await가 소개되었었고, 2015년에 Invoke & BeginInvoke 다루는 나도 참 늦게배운다 생각했는데, 2020년이 오는 중에도 Invoke와 BeginInvoke가 꾸준히 검색유입량1위다.
Invoke와 BeginInvoke가 나쁘다는 건 아니다. 여전히 필요로 하며, 특히 .NET 4.0, C# 5.0 이상을 사용할 수 없는 Visual Studio 환경에선 필수적이다. 그래도 소스코드는 간결하면 간결할 수록 유지보수하기 좋다는게 내 생각인지라, 앞으로 해당 글을 통해 유입되는 개발자분들에게 자연스럽게 안내해 드리고자 이번 포스트를 작성한다.
아래 소스코드는 "2015/01/11 - [C#] - [Invoke & BeginInvoke] 1. 다른 Thread 에서 UI 접근하기 (1)" 포스트의 예제 소스를 async, await와 Task 방식으로 변경한 소스이다.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Forms; namespace AsyncAwaitExample { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void btnCalc_Click(object sender, EventArgs e) { long cnt = 0; long result = 0; if (long.TryParse(txt_X.Text, out cnt)) { var calcTask = Task.Run(() => // 시대가 시대인지라 람다식으로 썼다. () => 대신 delegate() 로 바꿔도 무방하다. { result = Calc(cnt); }); await calcTask; // calcTask 가 완료될때까지 대기. 완료되면 바로 다음줄인 lbl_Result.Text = result.ToString();가 실행된다. lbl_Result.Text = result.ToString(); } else { lbl_Result.Text = "지정한 숫자가 잘못되었습니다."; } } private long Calc(long cnt) { long result = 0; for (long i = 0; i < cnt; i++) { result += 2; } return result; } } }
변경된 사항은 btnCalc_Click 함수 뿐이다. (using 네임스페이스 달라진건 애교로 봐달라.)
Click 이벤트 함수에 async 와 calcTask에 await가 붙은 것에 주목해야한다.UI 이벤트 함수에 async가 붙으면 이 함수는 UI 스레드에 비동기적으로 동작해야하는 작업이 있음을 의미한다. 해당 예제에선 calcTask가 비동기작업이다. Task.Run() 함수는 람다식 내부의 작업을 실행한다는 뜻을 의미한다. 지난 예제의 Thread.Start() 와 역할이 비슷하나, async 없이 Task.Run()만 호출하면 작업을 UI Thread에서 실행하게되는 차이가 있다.
await 는 대기를 의미한다. Run함수로 생성한 calcTask 작업이 모두 끝날때까지 await calcTask 소스코드의 다음줄에 있는 작업을 진행하지 못하도록 로직을 대기시킨다. 따라서 await 전에 결과값을 대입하려는 행위를 하면 원하는 결과를 화면에 표시할 수 없으니 주의해야한다.
lbl_Result.Text = result.ToString(); // 이러면 Calc 완료전 값인 0 값만 나오게된다. await calcTask; // calcTask 가 완료될때까지 대기. 완료되면 바로 다음줄인 lbl_Result.Text = result.ToString();가 실행된다.
로직을 대기시킨다고 해서 UI 가 멈추는 것은 아니므로 별도의 취소기능을 구현하여 작업을 조기종료시키도록 유도할 수도 있다.
Invoke 때 설명했던 것처럼, UI Thread(또는 Main Thread라고도 한다.)가 아닌 곳에서 직접 UI 컨트롤을 수정하는 행위는 허용하지 않으므로, Task 안에 직접 UI를 편집하는 코드를 넣어선 안된다.
끝!
반응형'.NET > 개념 및 유용한 팁' 카테고리의 다른 글
[ASP.NET Core] "An error occurred while starting the application." 떴을 때 상세 오류 확인방법 (0) 2021.01.09 [Entity Framework] MySQL 에서 Entity Framework 사용 (0) 2020.03.16 [WCF] 3. 이중계약으로 콜백기능 구현하기 (0) 2015.04.05 [WCF] 2. WCF 개념 (0) 2015.03.28 [WCF] 1. Helloworld (2) 2015.02.18