2014-06-12 4 views
4

나는 다음과 같은 코드를 많이 가지고 : 당신이 볼 수 있듯이이 코드를 비동기로 변환하는 방법?

 var feed = new DataFeed(host, port); 
     feed.OnConnected += (conn) => 
     { 
      feed.BeginLogin(user, pass); 
     }; 
     feed.OnReady += (f) => 
     { 
      //Now I'm ready to do stuff. 
     }; 

     feed.BeginConnect(); 

, 내가 비동기 작업을 수행하는 일반적인 방법을 사용합니다. async await을 사용하려면이 코드를 어떻게 변경합니까? 다음과 같은 형식이 바람직합니다.

public async void InitConnection() 
{ 
    await feed.BeginConnect(); 
    await feed.BeginLogin(user, pass); 

    //Now I'm ready 
} 
+0

BeginXXX 작업이 IAsyncResult를 반환합니까? –

+0

@SergeyBerezovskiy : 예를 들어'BeginConnect'는 void를 반환합니다. 내부적으로'Socket.BeginConnect'를 호출하고'Socket.EndConnect'가 호출되기를 기다립니다. 호출되면 OnConnected 이벤트가 시작됩니다. – nakiya

+2

비동기적인 목적에 맞지 않는 그런 종류의가요? 'Connect'를 철저히 부르는 것과 다른 점은 무엇입니까? – Luaan

답변

9

TaskCompletionSource<T>을 사용하여 EAP (이벤트 기반 비동기 패턴)를 작업으로 래핑 할 수 있습니다. (sample를) 그것은 당신이 오류를 처리하고의 datafeed 클래스에서 작업을 취소하는 방법 명확하지 않다, 그래서 당신이 코드를 수정하고 오류 처리를 추가해야합니다 :

public async void InitConnection() 
{ 
    var feed = new DataFeed(host, port); 
    await ConnectAsync(feed); 
    await LoadAsync(feed, user, pass); 
    //Now I'm ready 
} 
:

private Task ConnectAsync(DataFeed feed) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    feed.OnConnected += _ => tcs.TrySetResult(null); 
    feed.BeginConnect(); 
    return tcs.Task; 
} 

private Task LoginAsync(DataFeed feed, string user, string password) 
{ 
    var tcs = new TaskCompletionSource<object>(); 
    feed.OnReady += _ => tcs.TrySetResult(null); 
    feed.BeginLogin(user, pass); 
    return tcs.Task; 
} 

지금 당신은이 방법을 사용할 수 있습니다

주 - 이러한 비동기 메소드를 DataFeed 클래스로 이동할 수 있습니다. 하지만 DataFeed를 수정할 수 있다면 TaskFactory.FromAsync을 사용하여 APM API를 할 일 목록에 추가하는 것이 좋습니다.

불행히도 Task이 아닌 일반 TaskCompletionSource을 반환하지 않으므로 일반적으로 해결 방법은 Task<object>입니다.

+0

당신의 메소드에서'async' 키워드가 보이지 않습니다. 하나 있으면 안되나요? – nakiya

+2

+1, 이건 'TaskCompletetionSource '입니다. 한 가지 문제는 실패시 어떻게됩니까? 관심사가 없으면'TaskCompletetionSource '대'TaskCompletetionSource '를 사용하면 어떤 이점이 있습니까? – Jodrell

+0

@nakiya 아니, 그들은 아무것도 "기다리고"하지 않습니다. 'Task'를 리턴하는 함수는 "awaitable"입니다. – Jodrell

0

DataFeed 클래스를 변경해야합니다. 거기에 비동기 패턴 작업 만 사용해야합니다. 즉, DataFeed의 모든 비동기 메서드는 Task (또는 일부 Task<T>)을 반환해야하며, 예를 들어 ConnectAsync으로 지정해야합니다. SocketXXXAsync 방법이 실제로 awaitable하지 않기 때문에

지금, Socket, 이것은 완전히 쉬운 일이 아닙니다!

public async Task<bool> LoginAsync(TcpClient client, ...) 
{ 
    var stream = client.GetStream(); 

    await stream.WriteAsync(...); 

    // Make sure you read all that you need, and ideally no more. This is where 
    // TCP can get very tricky... 
    var bytesRead = await stream.ReadAsync(buf, ...); 

    return CheckTheLoginResponse(buf); 
} 

후 바로 외부에서 사용 :이 그냥 샘플 코드는 내가

await client.ConnectAsync(...); 

if (!(await LoginAsync(client, ...))) throw new UnauthorizedException(...); 

// We're logged in 

입니다 가장 쉬운 방법은 단순히 TcpClientTcpListener respectivelly을 (당신은 TCP를 사용하고 제공)를 사용하는 것입니다 실제로 시작하기에 알맞은 TCP 코드를 작성할 수 있다고 가정하십시오. 그렇다면 await을 사용하여 비동기 적으로 쓰는 것이 훨씬 더 어렵지는 않습니다. 비동기 I/O 작업을 항상 기다리고 있는지 확인하십시오. 매우 편리합니다 - 당신은 그냥 Socket를 사용하여 동일한 작업을 수행하려면

, 당신은 아마도 BeginXXX/EndXXX 방법의 래퍼를 제공 Task.FromAsync을 사용해야합니다.

+0

로그인 응답을 받기 전에도 서버에서 하트 비트 및 기타 등등을 수신하므로 이상적이지 않습니다. – nakiya

+0

@nakiya 글쎄, 네, 그래서 쓰기와 읽기를 직접 사용하지 않아야합니다. 메시지를 처리하는 메시지 프로세서가 있어야하며이를 기다려야합니다. 따라서'await stream.ReadAsync' 대신'messageProcessor.WaitForMessageAsync (...)'또는 무엇인가 기다리고 있습니다. 그것은 당신이 기다릴 수있는 가능성을 지원하는데 얼마나 깊숙한가에 관한 것입니다. 당신이 천천히 가고 싶다면, Sergey가 제안한 것처럼 'TaskCompletionSource'에서 이벤트를 감싸는 것이 좋습니다. 너가 모두를 대기 패턴에 바꾸는 것을 꺼리지 않으면, 내 길은 더 좋을지도 모른다. 그러나 더 많은 배관이 필요합니다. – Luaan

+0

언급 한 'WaitForMessageAsync '를 어떻게 구현할 수 있습니까? 나는 혼란 스럽다. 아마도 예제를 제공 할 수 있습니까? – nakiya

관련 문제