2012-11-26 2 views
1

비동기 JSON-RPC 프로토콜을 사용하는 응용 프로그램의 네트워크 계층을 구현하고 있습니다.WinRT : 차단 작업

서버와 통신하기 위해 적절한 요청을 보내고 서버가 응답을 보낼 때까지 기다린 다음 반환하는 방법을 만들고 싶습니다. 비동기/키워드 사용을 모두 사용합니다.

여기에 간단한 예제 코드입니다 :

문자열 응답;

Task<string> SendRequest(string methodName, string methodParams) 
{ 
    string request = generateRequest(methodName, methodParams); 

    await Send(request); // this will send using DataWriter, and StreamSocket 

    // block Task until response arrives 

    return response; 
} 


async void ReceiveLoop() 
{ 
    while (true) 
    { 
      uint numStrBytes = await _reader.LoadAsync(BufferSize); 

      string msg = _reader.ReadString(numStrBytes); 

      response = msg; 

      // unblock previously blocked SendRequest 
     }   
    } 
} 

async void main() 
{ 
    RecieveLoop(); 
} 

async void SendButtonPressed() 
{ 
    string response = await SendRequest("Test method", "Test params"); 

    Debug.WriteLine("Response = " + response); 
} 

이 패턴과 관련된 주요 문제는이 차단 조치입니다. 이 작업은 현재 작업을 차단하고 시간 제한을 처리 할 수 ​​있도록합니다. ManualResetEvent 및 WaitOne (int)을 사용하여 처리하려고 시도했지만 전체 스레드가 고정되고 비동기/대기 만 사용하기 때문에 전체 응용 프로그램이 고정됩니다 (UI 스레드가 더 정확함).

나에게 꽤 해킹 된 해결책은 CancellationTokens와 함께 Task.Delay를 사용할 수 있다는 것입니다. 모든 처리 할 필요가 던져진 excetion이,이 요청 (이 경우에 생략 -

... 
CancellationTokenSource cts; 
int timeout = 10000; 

Task<string> SendRequest(string methodName, string methodParams) 
{ 
    ... (prepare request, and send) 

    cts = new CancellationTokenSource(); 

    try 
    { 
     await Task.Delay(timeout, cts.Token); 
    } catch(TaskCanceledException) 
    { 

    } 

    // do rest 
} 

async void ReceiveLoop() 
{ 
    // init recieve loop, and recieve message 

    cts.Cancel();  
} 

(가 해킹처럼 보인다 외에) 성능입니다 솔루션의 문제 :

그것은 다음과 같습니다). 이것은 느리고 아파요 :

어떻게하면 더 우아한 방법으로 할 수 있습니까? 작업을 차단할 다른 옵션이 있습니까? 나는 당신의 정확한 요구 사항을 모르기 때문에 당신도 다음 루프를 필요로하는 경우

while(!cancel) { 
    await sendRequest(); 
    await receiveResponse(); 
} 

확실하지 :

답변

1

는 송신 버튼을 따라 트리거 하나 개 루프에 수신 및 전송 루프를 변환합니다. 다음과 같이 보일 수 있습니다.

await sendRequest(); 
await receiveResponse(); 

이는 단일 요청 - 응답 - 사이클을 실행합니다.


아래에서 귀하의 의견을 읽고 다음을 추가하고 싶습니다. 이제 두 개의 루프가 필요합니다. 나는 첫 비행에 요청을 표현하는 클래스를 만들어이 문제를 해결할 것 :

class Request { int ID; object Response; TaskCompletionCource CompletedTask; } 

뭔가를 보내, 당신은이 클래스의 인스턴스를 생성하고 Dictionary<int, Request>에 추가합니다. ID은 서버가 요청에 응답하는 데 사용할 수있는 공유 식별자입니다 (여러 요청이 걸출 할 수 있음을 이해합니다).

응답을 받으면 결과를 저장하고 CompletedTask을 완료로 표시합니다. 사워 보내기 방법은 다음과 같습니다.

var request = CreateRequest(); 
await sendRequest(request); 
await request.CompletedTask.Task; 
return request.Response; 

TaskCompletionSource은 이벤트로 사용됩니다. Request 클래스는 모든 관련 데이터를 캡슐화합니다.좋아

+0

이 앱은 양방향 JSON-RPC를 사용하므로 서버에서 언제든지 원격 메소드를 실행할 수 있으므로 루프를 수신해야하는 문제가 있습니다. 앱에서 항상 수신 메일을 찾아야합니다. – Axadiw

+0

나는 이것에 대한 나의 생각을 덧붙였다. – usr

+0

좋아, 고마워,하지만 전에 언급했듯이, 나는 타임 아웃을 처리해야한다. TaskCompletionSource-s가 너무 오래된 별도의 스레드를 검사하는 것보다 더 우아하게 처리 할 수있는 방법이 있습니까? – Axadiw

0

,

는 USR의 도움으로, 나는이 코드를 작성 관리했습니다 그 타임 아웃 블록 현재 작업 :

TaskTimeout 확장 :

internal struct VoidTypeStruct{} 

public static class TaskTimeoutExtension 
    { 
     public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) 
     { 
      if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout))) 
       await task; 
      else 
       throw new TimeoutException(); 
     } 
    } 

작업을 차단 :

var tcs = new TaskCompletionSource<VoidTypeStruct>(); 

try 
{ 
    await Task.Run(async() => { await tcs.Task; }).TimeoutAfter(RequestTimeout); 
} 
catch (TimeoutException) 
{       
} 

// store tcs variable somewhere 

작업을 차단 해제 :

tcs.SetResult(new VoidTypeStruct()); 

매우 빠르게 작동합니다 (이전 솔루션보다 훨씬 우수함).

마지막 질문에서 : 정말, 작업을 차단할 다른 방법이 없습니까? 중요한 섹션은 무엇입니까, 작업에 대한 뮤텍스가 있습니까?