2013-07-14 4 views
1

내가 이해하는 한, TPL Dataflow는 .NET 프로그래머 용 Actor 프로그래밍 모델을 제공합니다 (이전에 사용 가능했던 타사 솔루션에 대해서는 언급하지 않았습니다). 액터 모델 자체는 각 액터가 지원할 수있는 세 가지 기본 작동 인 '전송', '생성'및 '생성'이 있음을 선언합니다. TPL Dataflow에서 '될'의미를 다루는 '올바른'방법은 무엇입니까?TPL Dataflow : '의미있는 (Become)'의미

static void TestBecome() 
{ 
    TransformBlock<string, string> dispatcher = null; 
    dispatcher = new TransformBlock<string, string> 
    (
     val => 
     { 
      Console.WriteLine("Received for processing {0}", val); 

      switch (val) 
      { 
       case "CREATE": // create linked node 
       { 
        dispatcher.LinkTo(CreateNewNode().Item2); 
        break; 
       } 
       case "BECOME": // transform the node ('become' semantics) 
       { 
        var newNode = CreateNewNode(); 
        Console.WriteLine("Dispatcher transformed to {0}", newNode.Item1); 
        dispatcher = newNode.Item2; 

        break; 
       } 

       default: return val; // dispatch the value to linked node (one of) 
      } 

      return string.Empty; // 'empty unit' 
     } 
    ); 

    dispatcher.SendAsync("CREATE").ContinueWith(res => Console.WriteLine("Send CREATE: {0}", res.Result)); 
    dispatcher.SendAsync("CREATE").ContinueWith(res => Console.WriteLine("Send CREATE: {0}", res.Result)); 
    dispatcher.SendAsync("msg1").ContinueWith(res => Console.WriteLine("Send msg1: {0}", res.Result)); 
    dispatcher.SendAsync("msg2").ContinueWith(res => Console.WriteLine("Send msg2: {0}", res.Result)); ; 

    Thread.Sleep(1000); 

    dispatcher.SendAsync("BECOME").ContinueWith(res => Console.WriteLine("Send BECOME: {0}", res.Result)); ; 

    Thread.Sleep(1000); 

    dispatcher.SendAsync("msg3").ContinueWith(res => Console.WriteLine("Send msg3: {0}", res.Result)); 
    dispatcher.SendAsync("msg4").ContinueWith(res => Console.WriteLine("Send msg4: {0}", res.Result)); 
    dispatcher.SendAsync("msg5").ContinueWith(res => Console.WriteLine("Send msg5: {0}", res.Result)); 
} 

static Tuple<string, TransformBlock<string, string>> CreateNewNode() 
{ 
    var id = Guid.NewGuid().ToString("N"); 
    var node = new TransformBlock<string, string> 
    (
     val => 
     { 
      if (string.IsNullOrWhiteSpace(val)) // pass trough 'empty unit' 
       return val; 
      Console.WriteLine("NODE {0}: {1}", id, val); 
      return val; 
     } 
     , new ExecutionDataflowBlockOptions { BoundedCapacity = 3 } 
    ); 

    return Tuple.Create(id, node); 
} 

나는 서투른 '가'같은 방법을 찾을 :

은 다음 예제를 고려하십시오 대신 배우의 동작을 변경, 나는이 (원하지 않는 효과로 이어질 수있는) 그는 배우 인스턴스 자체를 변경합니다. '옳은'길은 무엇입니까?

다른 한 가지 질문 : 표준 TDF 블록은 링크 된 노트 (예 : ActionBlock)에 메시지를 전달하지 않습니다. 그렇지 않으면 로직이 수동으로 작성되지 않습니다. (대다수의 블록). 메시지가 어떤 경우에만 (항상은 아님) 보내질 때 논리가 사용자 정의 블록으로 구현되어야한다는 논리가 맞습니까?

답변

2

먼저 TPL 데이터 흐름은 액터 모델을 기반으로하지만 대부분의 경우 특정 부분 집합 만 사용하게됩니다. 예를 들어, 메시지를받은 후에 새로운 데이터 흐름 블록을 만들거나 메시지를 보낼 블록을 결정하는 것은 거의 없습니다 (링크가 일반적으로 사용됩니다). 비록 당신이 그것을 원한다면, 당신은 할 수 있습니다.

둘째로, 네가하는 일은 매우 서툴러 많은 경우에 효과가 없을 수 있습니다 (예 : 일부 블록이 이미 dispatcher에 연결되어있는 경우).

이렇게하려면 블록에서 실행되는 대리자가 내부 상태를 가질 수 있다는 사실을 사용합니다. 단순한 경우, 람다에서 캡쳐 된 변수를 사용할 수 있습니다. 당신이 string에 GUID를 앞에 추가 블록이 있다면 예를 들어, : 더 복잡한 경우에

var id = Guid.NewGuid().ToString("N"); 
var block = new TransformBlock<string, string>(
    input => 
    { 
     if (input == "BECOME") 
     { 
      id = Guid.NewGuid().ToString("N"); 
      return string.Empty; 
     } 

     return string.Format("{0}: {1}", id, input); 
    }); 

을, 당신은 필드에 상태를 저장하는 클래스를 만들고 그 인스턴스 메서드에 대리자를 만들 클래스 :

class IdPrepender 
{ 
    private string id = Guid.NewGuid().ToString("N"); 

    public string Process(string input) 
    { 
     if (input == "BECOME") 
     { 
      id = Guid.NewGuid().ToString("N"); 
      return string.Empty; 
     } 

     return string.Format("{0}: {1}", id, input); 
    } 
} 

… 

var block = new TransformBlock<string, string>(new IdPrepender().Process); 

두 경우 모두 블록을 병렬로 실행할 수 있으면 코드가 스레드로부터 안전한지 확인해야합니다.

또한이 경우 string을 오버로드하지 않을 것입니다. 두 번째 경우에는 TPL Dataflow가 순수 배우 모델이 아니며 클래스에 "become"메서드 (IdPrepender)를 추가한다는 사실을 활용할 수 있습니다.

메시지가 어떤 경우에만 전송되는 경우 (항상은 아님)의 논리가 맞춤 블록으로 구현되어야합니까?

사용자 지정 블록이 필요하지 않습니다. 대리자가 항상 0 또는 1 개의 항목을 반환하는 TransformManyBlock을 사용할 수 있습니다.

+0

답변 해 주셔서 감사합니다. 나는 두 가지의 추가 질문을 던졌다 : 1) '블록이 병렬로 실행될 수 있다면, 당신은 ..'- 내부 대기열에서 처리하기 위해 작업을 하나씩 취하는 배우의 특성이 아니므로 전역 공유 상태가 존재하지 않으면 경쟁 조건이 불가능합니다. 즉, 캡처 된 상태 변수가 액터 당 하나 인 경우)? 2) TDF가 70 년대에서 80 년대에 발명 된 '학술적'배우 모델에 더 가깝게 접근해야 하는가, 아니면 실질적인 목표를 따르므로 그 역할이 훌륭 할까? –

+1

1.'MaxDegreeOfParallelism'을 1이 아닌 값으로 설정하지 않는 이상입니다. 따라서 특정 블록 안에서 병렬 처리를하지 않으면 걱정할 필요가 없습니다. 2. 나에게 TDF에서의 방식은 의미가있다. 그러나 저는 다른 배우 모델 라이브러리 나 언어를 사용하지 않았습니다. – svick