2013-03-21 3 views
2

저는 새 작가이기 때문에 저와 함께 참아 요.WCF 듀플렉스 내부의 TPL 데이터 흐름 블록

이중 서비스 계약을 맺은 WCF 서비스가 있습니다. 이 서비스 계약에는 장기간의 데이터 처리를 가정하는 운영 담당자가 있습니다. 최대 3 번까지 처리 할 수있는 동시 데이터 처리 수를 제한하려고합니다. 문제는 데이터 처리 후 동일한 서비스 인스턴스 컨텍스트로 돌아가서 데이터 처리 결과를 전달하는 이니시에이터 엔드 포인트를 다시 호출해야한다는 것입니다. 여러 가지 이유로 TPL 데이터 흐름과 WCF 이중화에 제약이 있다고 언급 할 필요가 있습니다. 여기

내가 WCF 여기

class Program 
{ 
    static void Main(string[] args) 
    { 
     // simulate service calls 

     Enumerable.Range(0, 5).ToList().ForEach(x => 
     { 
      new System.Threading.Thread(new ThreadStart(async() => 
      { 
       var service = new Service(); 
       await service.Inc(x); 
      })).Start(); 
     }); 
    } 
} 

를 호출하는 WCF 서비스

// service contract 
public class Service 
{ 
    static TransformBlock<Message<int>, Message<int>> transformBlock; 

    static Service() 
    { 
     transformBlock = new TransformBlock<Message<int>, Message<int>>(x => Inc(x), new ExecutionDataflowBlockOptions 
     { 
      MaxDegreeOfParallelism = 3 
     }); 
    } 

    static Message<int> Inc(Message<int> input) 
    { 
     System.Threading.Thread.Sleep(100); 

     return new Message<int> { Token = input.Token, Data = input.Data + 1 }; 
    } 

    // operation contract 
    public async Task Inc(int id) 
    { 
     var token = Guid.NewGuid().ToString(); 

     transformBlock.Post(new Message<int> { Token = token, Data = id }); 

     while (await transformBlock.OutputAvailableAsync()) 
     { 
      Message<int> message; 
      if (transformBlock.TryReceive(m => m.Token == token, out message)) 
      { 
       // do further processing using initiator service instance members 
       // something like Callback.IncResult(m.Data); 
       break; 
      } 
     } 
    } 
} 

public class Message<T> 
{ 
    public string Token { get; set; } 

    public T Data { get; set; } 
} 

에게로 가정 무엇 시뮬레이션 내가 콘솔 라이브러리에서는 지금까지

쓴 것과 데모입니다 운영 계약은 실제로 비동기 일 필요는 없지만 OutputAvailableAsync 알림이 필요합니다.

이것은 좋은 접근 방법입니까 아니면 제 시나리오의 더 나은 해결책입니까? 사전에

감사합니다.

+0

"나는 TPL의 데이터 흐름을 제약하고있다"당신은 무엇을 의미합니까? 데이터 흐름을 사용해야 만합니까? 왜? 그건별로 의미가 없습니다. – svick

+0

데이터 처리에 대한 필자의 요구 사항은 동시성을 보장하면서 병렬 처리를 제한하는 것입니다. TPL 데이터 흐름 블록은 PLinq 또는 다른 것 대신 기술적 요구 사항에 부과되는 좋은 선택처럼 보입니다. – uni3324

+0

이것이 정말로 데이터 흐름을 사용하는 것이라면 과도하다고 생각합니다. 보다 간단한 코드로 동일한 효과를 얻을 수 있습니다 (제 답변 참조). 또한 양방향 서비스가 올바른 선택 인지도 확실하지 않습니다. 클라이언트와 서버 모두 비동기적일 수 있습니다. – svick

답변

1

먼저 토큰을 사용하는 방식으로 사용해서는 안됩니다. 고유 식별자는 프로세스 간 통신시 유용합니다. 그러나 단일 프로세스 안에 있으면 참조 평등을 사용하십시오.

실제로 질문에 대답하려면, (종류의) 바쁜 루프가 좋은 생각이 아니라고 생각합니다.

비동기식 제한에 대한보다 간단한 해결책은 SemaphoreSlim을 사용하는 것입니다. 같은 뭔가 :

static readonly SemaphoreSlim Semaphore = new SemaphoreSlim(3); 

// operation contract 
public async Task Inc(int id) 
{ 
    await Semaphore.WaitAsync(); 

    try 
    { 
     Thread.Sleep(100); 
     var result = id + 1; 
     // do further processing using initiator service instance members 
     // something like Callback.IncResult(result); 
    } 
    finally 
    { 
     Semaphore.Release(); 
    } 
} 

은 당신이 정말로 원하는 (?하거나이) 흐름을 사용하는 경우 작동 및 블록 사이의 동기화를 위해 TaskCompletionSource를 사용할 수 있습니다. 조작 방법은 TaskCompletionSourceTask에 기다릴 것이다 그리고 그 메시지에 대한 계산을 완료 할 때 블록을 설정합니다 :

private static readonly ActionBlock<Message<int>> Block = 
    new ActionBlock<Message<int>>(
     x => Inc(x), 
     new ExecutionDataflowBlockOptions 
     { 
      MaxDegreeOfParallelism = 3 
     }); 

static void Inc(Message<int> input) 
{ 
    Thread.Sleep(100); 

    input.TCS.SetResult(input.Data + 1); 
} 

// operation contract 
public async Task Inc(int id) 
{ 
    var tcs = new TaskCompletionSource<int>(); 

    Block.Post(new Message<int> { TCS = tcs, Data = id }); 

    int result = await tcs.Task; 
    // do further processing using initiator service instance members 
    // something like Callback.IncResult(result); 
} 
관련 문제