C # 5 비동기 지원은 UI 스레드 동기화 문제에 어떤 도움이됩니까?


16

어딘가에서 C # 5 async-await가 너무 멋져서이 작업에 대해 걱정할 필요가 없다는 것을 들었습니다.

if (InvokeRequired)
{
    BeginInvoke(...);
    return;
}
// do your stuff here

대기 작업의 콜백이 호출자의 원래 스레드에서 발생하는 것처럼 보입니다. Eric Lippert와 Anders Hejlsberg는이 기능이 UI (특히 터치 장치 UI)의 반응 속도를 높여야한다는 요구에서 비롯된 것이라고 여러 번 언급했습니다.

이러한 기능을 일반적으로 사용하는 방법은 다음과 같습니다.

public class Form1 : Form
{
    // ...
    async void GetFurtherInfo()
    {
        var temperature = await GetCurrentTemperatureAsync();
        label1.Text = temperature;
    }
}

콜백 만 사용하는 경우 레이블 텍스트를 설정하면 UI 스레드에서 실행되지 않으므로 예외가 발생합니다.

지금까지 나는 이것이 사실임을 확인하는 자료를 찾을 수 없었다. 누구든지 이것에 대해 알고 있습니까? 이것이 어떻게 작동하는지 기술적으로 설명하는 문서가 있습니까?

신뢰할 수있는 출처의 링크를 제공하십시오. "예"라고 대답하지 마십시오.


적어도 await기능에 관한 한, 가능성은 거의 없습니다 . 그것은 계속해서 지나가는 많은 구문 설탕입니다 . 아마도 WinForms에 도움이 될 다른 관련없는 개선이 있습니까? 그것은 C #이 아닌 .NET 프레임 워크 자체에 속합니다.
Aaronaught

@Aaronaught 동의합니다. 이것이 바로 질문을 정확하게하는 이유입니다. 나는 어디에서 왔는지 명확히하기 위해 질문을 편집했습니다. 그들이이 기능을 만들면서도 악명 높은 InvokeRequired 스타일의 코드를 사용해야한다는 것이 이상하게 들립니다.
Alex

답변:


17

여기 몇 가지 혼란스러워하는 것 같습니다. 당신은 무엇을 요구하는지 것은 이미 사용 가능한 System.Threading.Tasks1, asyncawaitC # 5 단지 같은 기능에 대한 좀 더 좋은 문법 설탕을 제공 할 것입니다.

Winforms 예제를 사용해 봅시다. 폼에 버튼과 텍스트 상자를 놓고이 코드를 사용하십시오 :

private void button1_Click(object sender, EventArgs e)
{
    Task.Factory.StartNew<int>(() => DelayedAdd(5, 10))
        .ContinueWith(t => DelayedAdd(t.Result, 20))
        .ContinueWith(t => DelayedAdd(t.Result, 30))
        .ContinueWith(t => DelayedAdd(t.Result, 50))
        .ContinueWith(t => textBox1.Text = t.Result.ToString(),
            TaskScheduler.FromCurrentSynchronizationContext());
}

private int DelayedAdd(int a, int b)
{
    Thread.Sleep(500);
    return a + b;
}

그것을 실행하고 당신은의 (a)는 UI 스레드와 (b) 당신이 오류 일반적인 "유효하지 크로스 스레드 작업을"하지 않습니다 차단하지 않습니다 볼 수 있습니다 - 당신은 제거하지 않는 TaskScheduler마지막에서 인수 ContinueWith에를 어떤 경우에

이것은 bog 표준 연속 전달 스타일 입니다. 마술은 TaskScheduler클래스에서, 구체적으로에 의해 검색된 인스턴스 에서 발생 합니다 FromCurrentSynchronizationContext. 이것을 연속으로 전달하면 FromCurrentSynchronizationContext메소드 라고하는 스레드 ( 이 경우 UI 스레드) 에서 연속을 실행해야한다고 알립니다 .

웨이터는 시작한 스레드와 연속이 필요한 스레드를 알고 있다는 점에서 약간 더 정교합니다. 따라서 위의 코드는 더 자연스럽게 작성 될 수 있습니다 .

private async void button1_Click(object sender, EventArgs e)
{
    int a = await DelayedAddAsync(5, 10);
    int b = await DelayedAddAsync(a, 20);
    int c = await DelayedAddAsync(b, 30);
    int d = await DelayedAddAsync(c, 50);
    textBox1.Text = d.ToString();
}

private async Task<int> DelayedAddAsync(int a, int b)
{
    Thread.Sleep(500);
    return a + b;
}

이 두 가지는 매우 비슷해 보이며 실제로 매우 비슷합니다. 이 DelayedAddAsync메소드는 이제 Task<int>대신을 반환 int하므로 await계속 각 연속으로 연속을 두드리는 것입니다. 가장 큰 차이점은 각 줄에서 동기화 컨텍스트를 전달한다는 것이므로 마지막 예에서와 같이 명시 적으로 수행 할 필요가 없습니다.

이론적으로는 차이점이 훨씬 더 중요합니다. 두 번째 예에서 button1_Click메서드의 모든 단일 행 은 실제로 UI 스레드에서 실행되지만 작업 자체 ( DelayedAddAsync)는 백그라운드에서 실행됩니다. 첫 번째 예에서는 모든 것을 실행은에 배경 , 제외시켰다 에 할당을 위해 textBox1.Text우리가 명시 적으로 UI 스레드의 동기화 컨텍스트에 첨부했습니다있다.

정말 흥미로운 await점은 대기자가 차단 호출없이 동일한 방법 으로 들어오고 나올 수 있다는 사실입니다 . 을 호출 await하면 현재 스레드가 메시지 처리로 돌아갑니다. 완료되면 대기자는 중단 된 것과 동일한 스레드에서 중단 된 위치를 정확하게 선택합니다.하지만 질문 의 Invoke/ BeginInvoke대비 측면 에서 나는 ' 오래 전에 그 일을 그만 두어야한다고 말해서 죄송합니다.


@Aaronaught가 매우 흥미 롭습니다. 나는 연속 전달 스타일을 알고 있었지만이 전체 "동기화 컨텍스트"에 대해서는 알지 못했습니다. 이 동기화 컨텍스트를 C # 5 async-await에 연결하는 문서가 있습니까? 나는 그것이 기존의 기능이라는 것을 이해하지만 기본적으로 그것을 사용한다는 사실은 특히 성능에 큰 영향을 미치기 때문에 큰 소리처럼 들립니다. 그것에 대한 추가 의견이 있으십니까? 그건 그렇고 답변 주셔서 감사합니다.
Alex

1
@Alex : 모든 후속 질문에 대한 답변은 비동기 성능 : 비동기 비용 및 대기 비용 이해를 참조하십시오 . "컨텍스트 정보 관리"섹션은 이것이 모두 동기화 컨텍스트와 관련되는 방법을 설명합니다.
Aaronaught

(그런데, 동기화 컨텍스트는 새로운 것이 아닙니다. 2.0 이후 프레임 워크에있었습니다. TPL은 그것들을 훨씬 더 사용하기 쉽게 만들었습니다.)
Aaronaught

2
여전히 InvokeRequired 스타일을 사용하는 것에 대해 많은 토론이 있고 궁금한 스레드 대부분이 동기화 컨텍스트를 언급하지 않는 이유가 궁금합니다. 이 질문을 제출할 시간을 절약 할 수 있었을 것입니다.
Alex

2
@ 알렉스 : 당신이 올바른 곳을보고 있지 않은 것 같아요 . 무엇을 말해야할지 모르겠습니다. .NET 커뮤니티의 많은 부분이 따라 잡는 데 오랜 시간이 걸립니다. 지옥, 나는 여전히 ArrayList새로운 코드 에서 클래스를 사용하는 일부 코더를 본다 . 나는 여전히 RX에 대한 경험이 거의 없다. 사람들은 이미 알고있는 내용이 최신이 아닌 경우에도 알아야 할 내용을 배우고 이미 알고있는 내용을 공유합니다. 이 답변은 몇 년 후에 구식이 될 수 있습니다.
Aaronaught

4

예, UI 스레드의 경우 대기 작업의 콜백은 호출자의 원래 스레드에서 발생합니다.

Eric Lippert는 1 년 전에 8 파트 시리즈를 썼습니다 : Fabulous Adventures In Coding

편집 : 여기 Anders의 // 빌드 / 프리젠 테이션이 있습니다 : channel9

BTW, "// build /"를 거꾸로 돌리면 "/ plinq //";-)


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.