C# 第三方物流数据流:';成为';语义学
据我所知,TPL数据流为.NET程序员提供了Actor编程模型(而不是以前可用的第三方解决方案)。Actor模型本身声明每个Actor可以支持三个基本操作:“send”、“create”和“been”。第三方物流数据流中处理“成为”语义的“正确”方式是什么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
请考虑以下样本:
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年代发明的“学术”演员模型描述,还是遵循实际目标和标准