C# 第三方物流数据流:';成为';语义学

C# 第三方物流数据流:';成为';语义学,c#,task-parallel-library,tpl-dataflow,C#,Task Parallel Library,Tpl Dataflow,据我所知,TPL数据流为.NET程序员提供了Actor编程模型(而不是以前可用的第三方解决方案)。Actor模型本身声明每个Actor可以支持三个基本操作:“send”、“create”和“been”。第三方物流数据流中处理“成为”语义的“正确”方式是什么 请考虑以下样本: static void TestBecome() { TransformBlock<string, string> dispatcher = null; dispatcher = new Tran

据我所知,TPL数据流为.NET程序员提供了Actor编程模型(而不是以前可用的第三方解决方案)。Actor模型本身声明每个Actor可以支持三个基本操作:“send”、“create”和“been”。第三方物流数据流中处理“成为”语义的“正确”方式是什么

请考虑以下样本:

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);
}
static void testbeen()
{
TransformBlock dispatcher=null;
dispatcher=新转换块
(
val=>
{
WriteLine(“接收以处理{0}”,val);
开关(val)
{
案例“创建”://创建链接节点
{
LinkTo(CreateNewNode().Item2);
打破
} 
case“been”://转换节点('been'语义)
{
var newNode=CreateNewNode();
WriteLine(“已转换为{0}的调度程序”,newNode.Item1);
dispatcher=newNode.Item2;
打破
}
默认值:return val;//将值分派到链接节点(其中一个)
}
返回字符串.Empty;//“空单位”
}
);
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));
睡眠(1000);
dispatcher.SendAsync(“been”).ContinueWith(res=>Console.WriteLine(“Send-been:{0}”,res.Result));
睡眠(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));
}
静态元组CreateNewNode()
{
var id=Guid.NewGuid().ToString(“N”);
var节点=新转换块
(
val=>
{
if(string.IsNullOrWhiteSpace(val))//通过“空单元”
返回val;
WriteLine(“节点{0}:{1}”,id,val);
返回val;
}
,新的ExecutionDataflowBlockOptions{BoundedCapacity=3}
);
返回Tuple.Create(id,node);
}
我发现这种“变得”笨拙的方式:我不是改变演员的行为,而是改变演员实例本身(可能导致不想要的效果)。“正确”的方法是什么


还有一个问题:据我所知,标准TDF块要么从不将消息传递给链接的注释(例如,ActionBlock,如果该逻辑不是手工编写的),要么总是这样做(大多数块)。只有在某些情况下(并非总是)才会发送消息的逻辑应该作为自定义块实现,这对吗?

首先,TPL数据流基于参与者模型,但大多数情况下,您将只使用它的特定子集。例如,在接收到消息后创建一个新的数据流块或决定将消息发送到哪个块(链接通常用于此目的)是非常罕见的。如果你想这样做,你可以

其次,是的,您所做的工作非常笨拙,在许多情况下都不起作用(例如,如果某个块已链接到
调度程序

要做到这一点,我将使用在块中执行的委托可以具有内部状态这一事实。对于简单的情况,可以在lambda中使用捕获的变量。例如,如果有一个块将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);
    });
var id=Guid.NewGuid().ToString(“N”);
var块=新转换块(
输入=>
{
如果(输入=“变成”)
{
id=Guid.NewGuid().ToString(“N”);
返回字符串。空;
}
返回string.Format(“{0}:{1}”,id,输入);
});
对于更复杂的情况,您可以创建一个将其状态存储在字段中的类,并创建该类的实例方法的委托:

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);
class-IdPrepender
{
私有字符串id=Guid.NewGuid().ToString(“N”);
公共字符串进程(字符串输入)
{
如果(输入=“变成”)
{
id=Guid.NewGuid().ToString(“N”);
返回字符串。空;
}
返回string.Format(“{0}:{1}”,id,输入);
}
}
…
var block=新的TransformBlock(新的IdPrepender().Process);
在这两种情况下,如果块可以并行执行,则必须确保代码是线程安全的

而且,我不会像这样重载
string
。在第二种情况下,您可以利用TPL数据流不是纯参与者模型这一事实,向类中添加一个“been”方法(
IdPrepender

只有在某些情况下(并非总是)才应发送消息的逻辑应作为自定义块实现,这是对的吗


您不需要为此自定义块。您可以使用
TransformManyBlock
,其委托始终返回0或1项。

谢谢您的回答!我还有两个问题:1)“如果块可以并行执行,那么您必须……”这难道不是actor的一个特性吗?它从内部队列一个接一个地处理任务,因此是线程安全的(如果不存在全局共享状态,也就是说,如果捕获的状态变量是每个actor一个,那么就不可能存在竞争条件)?2) 只是想知道,你的个人意见-TDF应该更接近于70-80年代发明的“学术”演员模型描述,还是遵循实际目标和标准