通道、域和消息并驾齐驱
最后一个本文我们看编程语言 Cobra,松散来自 Python embraces 这两个静态和动态-键入的面向对象的语言部分"脚本"类似于 Python 和拼音,和内置在单元测试功能,除了做其他事情外中找到的概念。 更深入的调查时,我们看到我们高兴地了解到了新的通用编程语言,它具有值。
为有趣且功能强大,因为通用语言,但是,有时所需的不是锤子、 锯或螺丝刀,而 3/16 英寸螺母驱动程序 ;换而言尽可能多的值提供 wide-ranging 功能和实用程序工具,我们作为开发人员有时作业的合适的工具是一个非常、 非常特定。 在此段,我们将重点不重新创建,我们正在用于,完成生产的结构的所有的语言,但侧重于一个特定区域的语言执行该操作也,查找在这种情况下并发。
该杂志的认真和全面的读者将请注意此并发 meme 不是一个不熟悉这些页。 在过去几年过通过并发大量文章此处,而且在 MSDN Magazine ** 几乎无法中是唯一的方面--在 blogosphere,Twitterverse 和开发人员 Web 门户所有 rife 并发,包括几个 naysayers 认为此整个并发是另一个试图创建的细空气出问题的讨论。 某些行业专家甚至已到目前为止,说 (根据为 rumor,在面板讨论在几年前的 OOPSLA 会议) 的并发是我们必须在未来的十年中 slay 在 dragon。
某些开发人员将问题确实什么想知道--.NET 后所有,多年通过异步委托调用用法 (BeginInvoke/EndInvoke) 具有多线程能力和提供对象的构造 (Mutex,事件和等等从 System.Threading) 通过监视器构造 (lock 关键字从 C# 示例) 和其他明确的并发线程的安全机制。 因此,所有此新 fuss 看起来使一个 molehill 超出一个山地车。 这些开发人员是绝对是正确假定它们编写代码,且 (1) 100%线程安全 (2) 利用的每个商机在其代码中的任务或数据并行度的旋转线程或借用从最大化的基础硬件提供的所有核心使用线程池线程。 (如果您在两个计数安全请转到在杂志文章。 最好还,编写一个和告诉我们您的机密信息。
许多方案解决,或者至少降低,此问题有被浮动通过 Microsoft 宇宙,包括任务并行库 (TPL)、 并行 FX 扩展 (PFX),F # 异步工作流,在并发和控制运行库 (CCR),等。 在每个这种情况下解决方案是为扩展方法以提供额外的并行性或提供一个库,可以从.NET 通用语言调用一个主机语言 (通常是 C#)。 对大多数这些的方法的缺点但是,是因为它们取决于主机语言的语义,从并发角度来看执行在右侧件事开发人员必须明确地购买到和适当的代码的可选选项。 这遗憾的是可能的执行错误操作,任何的商机,意味着因此创建代码可以最终分为,创建 Bug 被鍙戠幇等待一个并发漏洞,这是件坏事情。 开发人员工具应,在理想,允许开发人员执行错误操作--这是.NET 平台例如移动一个垃圾回收方法的原因。 而是,开发人员工具应导致开发人员,尝澶氶粠鍚勶 Mariani (Microsoft Visual Studio 架构师和以前的 CLR 性能架构师) 将其"分为成功的底坑"放入--开发人员查找最容易执行应在右侧件事和内容开发人员发现难以执行,或查找无法进行应该是错误的操作。
向结尾的 Microsoft 发布了为一种新语言称为"Axum 的并发域 squarely 针对 incubation 项目。"在该精神的使开发人员能够"分为的成功,在 Pit"Axum 不是通用语言 (如 C# 或 Visual Basic,一 squarely 旨在并发,从开始设计为一组共同协作以解决业务问题的语言的一部分的问题。 在实质的方式上 Axum 是语言的一个很好的示例,特定于域专为解决问题的一个垂直方面而设计的一种语言。
在撰写本文 Axum 标记为 0.2,版本和 Axum 的语言和操作的详细信息有完全更改,该免责声明在相应的文章的前面所述。 "按此写入"但之间和"按您阅读此,。核心概念应保持相当稳定。 实际上,这些概念是不唯一 Axum 中找到的其他语言和库 (包括上述项目,如 F # 和 CCR 的几个),即使 Axum 本身不会使它到广泛使用,此处将想法是并发思考的宝贵。 怎样的更一般的想法--语言特定于问题域--快速增长到"大作"为值得检查在其自己的右侧,但一个更高版本。
开始
msdn.microsoft.com/en-us/devlabs/dd795202.aspx ; 在 Microsoft 实验室 Web 站点上的当前活动下载 Axum 位一定要选取至少在.msi (或者为 Visual Studio 2008 或 Beta 1 的 Visual Studio 2010) 和程序员的指南。 一旦您安装这些,触发了 Visual Studio,并创建新 Axum 控制台应用程序项目,如图 1 的 中所示。
图 1 的 Axum 控制台应用程序项目
用的图 2 所示的代码替换它。 这是在 Axum"Hello World,"用途在同一个作为每个其他的 Hello World 应用程序执行操作:若要验证安装的工作并语法的基本了解。 假定所有编译并运行,我们最好转。
图 2 Axum"Hello World"
using System;
using Microsoft.Axum;
using System.Concurrency.Messaging;
namespace Hello
{
public domain Program
{
agent MainAgent : channel Microsoft.Axum.Application
{
public MainAgent()
{
String [] args = receive(PrimaryChannel::CommandLine);
// TODO: Add work of the agent here.
Console.WriteLine("Hello, Axum!");
PrimaryChannel::ExitCode <-- 0;
}
}
}
}
正如您所看到语法是 Axum 的高度 reminiscent 的 C#,将 Axum 松散地放入称为该系列 C 语言的语言的类别:它使用大括号表示作用域,用分号终止语句,等等。 说与,但是,有几个新运算符以及了解域、 代理程序通道,接收,对于初学者,他们只需),显然几个新关键字。 正式,Axum 不是真正的 C#,但有选择地添加新的功能子集一阶导数。 Axum 包括所有 (如 lambdas 和推断的局部变量) 的 C# 3.0 语句类型和表达式 (如代数计算和收益返回收益/中断)、 方法、 字段声明、 委托和枚举类型,但它的类、 接口,或结构/值类型定义,运算符声明、 属性、 常数字段/局部变量,和静态字段/方法保留。
听到该 whooshing 声音吗? 这 rushing 过去您耳朵,在风,如果您认为轻微 fluttering 在您感到,确定。 关闭 cliffs 跳转是件好事。
概念和语法
只是一样的 C#,Axum 语法直接反映 Axum 的核心概念。 在 C# 具有类,但是,Axum 具有代理。 -Axum,在代理程序是代码,实体,但与一个传统对象对象不同代理程序永远不会直接交互-将是,他们邮件传递到另一个以异步方式通过通道,或是更准确地定义该通道上端口。 在端口上发送消息是异步操作--发送立即返回--并且接收一条消息之前的一个端口块上的一条消息。 这种方式,线程永远不会交互在相同的数据元素上一次并有效地离开提升死锁和不一致的主要源。
在操作中看到此,考虑 Hello 代码。 代理 MainAgent 实现调用应用是通道 Axum 运行时直接--了解它将处理传入的任何命令行参数,并将它们传递 CommandLines 端口上的应用程序的 PrimaryChannel 通道上的程序的特殊通道。 当运行时构造该 MainAgent 使用接收操作 (Axum 基元) 阻止该端口,直到可用这些参数时,此时继续 MainAgent 构造函数中的执行。 运行库,具有完成其作业,然后在 PrimaryChannel,块 ExitCode 端口上的这是应用程序的已完成信号。 同时,通过打印每个的该数组和完成时,在 MainAgent 回滚将整数 0 发送到 ExitCode 端口中。 这样,运行库然后会终止该进程被接收。
请注意如何 Axum,非常从头占用并发执行这种做法会严重--不只从创建的开发人员 sheltered 并操作的线程或并发/锁定的对象,但 Axum 语言甚至的 Hello 一样简单的事情假定一个并发的方法。 某些 Hello 一样简单,证明,这是浪费时间--但是,我们的大多数不经常,编写 Hello 一样简单的应用程序和那些执行,可以始终回退到传统的面向对象的单个线程的情况的默认应用程序语言 (C# 或 Visual Basic 或任何其他 strikes 您别致)。
图 3 的进程的应用程序
using System;
using Microsoft.Axum;
using System.Concurrency.Messaging;
namespace Process
{
public domain Program
{
agent ProcessAgent : channel Microsoft.Axum.Application
{
public ProcessAgent()
{
String [] args = receive(PrimaryChannel::CommandLine);
for (var i = 0; i < args.Length; i++)
ProcessArgument(args[i]);
PrimaryChannel::ExitCode <-- 0;
}
function void ProcessArgument(String s)
{
Console.WriteLine("Got argument {0}", s);
}
}
}
}
说 Hello 用于 Wimps
一个更为有趣的示例是处理应用程序图 3 所示。 在此的版本中,我们将看到一个新的 Axum 概念函数。 函数是不同的传统方法 Axum 编译器保证 (方法的编译器错误) 此方法永远不会修改代理的内部状态 ;换而言函数可能没有副作用。 防止副作用向 Axum 的用途为并发易于语言--没有副作用,变得极难意外地编写代码的线程修改共享的状态 (并因此引入不一致或不正确的结果)。
但我们仍未完成此处。 Axum 不仅希望更容易地编写线程安全的代码还想要更容易地编写适合线程的代码。 成为更多的主流和 8 和 16 核心计算机显示在该范围上的两个和四个核心计算机与重要的寻找机会以并行方式执行操作。 Axum 简化这,再次通过提升线程离开对话,并提供使用某些更高级别的结构。 图 4 显示该 ProcessAgent 的修改的版本。
此处发生两个新内容:一个,我们创建了可用作源或邮件的收件人的交互点 (在这种情况下它将两个) ;和两个,我们 ProcessArgument 函数从交互点创建的消息的管道中使用转发运算符。 通过执行此发送到"参数"的所有邮件操作交互点将被转发到的 ProcessArgument 也,处理。 从那里,剩下的是循环访问命令行参数,并发送邮件的每个 (通过将发送运算符在 <--在循环内的操作) 参数的交互作用点,和工作开始。
(Axum 正式定义此数据流网络,和还有很多它如何另外创建带简单的 1-1 管道但这超出了本简介的范围。 程序员的指南具有更多详细信息对于那些感兴趣的人员。
图 4 修改版本 ProcessAgent
using System;
using Microsoft.Axum;
using System.Concurrency.Messaging;
namespace Process
{
public domain Program
{
agent ProcessAgent : channel Microsoft.Axum.Application
{
public ProcessAgent()
{
String [] args = receive(PrimaryChannel::CommandLine);
// Second variation
//
var arguments = new OrderedInteractionPoint<String>();
arguments ==> ProcessArgument;
for (var i = 0; i < args.Length; i++)
arguments <-- args[i];
PrimaryChannel::ExitCode <-- 0;
}
function void ProcessArgument(String s)
{
Console.WriteLine(“Got argument {0}”, s);
}
}
}
}
将现在数据放置通过管道,Axum 可以决定何时以及如何将数值调节钮线程进行处理。 在此简单的示例中,可能不是很有用的或有效的方式执行此操作,,但对于更复杂的方案,它经常将。 由于参数交互是一个有序的交互鎸囧悜管道--发送邮件,并且很多重要结果返回--将保留其位置在排序中的即使在单独的线程上处理每个消息。
怎么的更多的管道传送到另一个交互点来自此概念是一个功能强大的概念,Axum 借用类似 F # 的函数式语言中。 图 5 显示如何轻松它添加到管道一些额外的处理。
请注意,再次,UpperCaseIt 函数不修改内部 ProcessAgent 状态但是只能在传递的对象上运行。 此外,管道被修改之前,ProcessArgument 函数包含 UpperCaseIt 和直观的事情发生--UpperCaseIt 的结果传递到 ProcessArgument 函数一次。
作为一个精神的练习考虑 C# 代码的行数将需要这样做,带有记住所有正在也进行跨多个的线程而不只是一个单个执行线程。 现在假设调试以确保它正确执行代码。 -(实际上,imagining 它不需要-使用 ILDasm 或反射器类工具来检查生成的 IL。 也就明确不常用)。
通过在方法有一个小的表述对--写入,我的示例不能正确执行。 当运行前面的代码时,应用程序将不显示任何内容的情况下返回。 这不是 Bug Axum 位 ;这将是为预期的行为,并突出显示了如何在一个并发的思维方式的编程需要一精神移位。
参数交互点时接收发送操作员通过命令行参数 (<--),异步完成的发送。 在 ProcessAgent 不阻止发送这些的邮件时,因此如果管道足够复杂,参数所有已发送,将终止循环并发送该 ExitCode,终止该应用程序所有之前任何可以访问控制台。
若要解决此问题,ProcessAgent 需要阻塞,直到管道已完成的操作 ;它需要保存在线程处于活动状态与一个 Console.ReadLine() 类似。 (这证明会比较棘手实际上--请参阅有关详细信息,Axum 团队博客)。或需要更改 ProcessAgent 的工作方式,我打算为了,主要演示 Axum 的功能的几个更多的后一种本课。
而不是执行本身内的工作,ProcessAgent 将推迟到新的代理程序工作。 但现在,只是为了使事情更有趣的情况下,该新代理程序将还想知道是否参数应为大写或小写。 涓烘 新代理需要定义一个更复杂的消息,采用不仅上, 操作字符串的但也一个布尔值 (真的大写)。 要做到这一点,Axum 要求我们定义架构。
图 5 管道消息
using System;
using Microsoft.Axum;
using System.Concurrency.Messaging;
namespace Process
{
public domain Program
{
agent ProcessAgent : channel Microsoft.Axum.Application
{
public ProcessAgent()
{
String [] args = receive(PrimaryChannel::CommandLine);
// Third variation
//
var arguments = new OrderedInteractionPoint<String>();
arguments ==> UpperCaseIt ==> ProcessArgument;
for (var i = 0; i < args.Length; i++)
arguments <-- args[i];
PrimaryChannel::ExitCode <-- 0;
}
function String UpperCaseIt(String it)
{
return it.ToUpper();
}
function void ProcessArgument(String s)
{
Console.WriteLine("Got argument {0}", s);
}
}
}
}
处理我的参数
隐式直到该点,Axum 的目标之一有找出另一个,离开的不同执行实体 (代理) 后, 它具有执行该操作使邮件的副本和处理这些代理程序,而不是直接传递对象。 不共享的状态--通过参数--即使意味着没有意外的机会的线程冲突。
时的正常的.NET 基类库中定义的类型,它可以很容易地是失效如果.NET 程序员不执行用户定义的类型中,右事情。 若要克服此,Axum 需要作为架构类型--实质上是一个对象与该方法不需要和/或可选的字段使用架构关键字定义的一个新类型的消息:
schema Argument
{
required String arg;
optional bool upper;
}
这将定义一个新的数据类型,参数,必填的字段、 arg,包含字符串的大写或小写,进行和可选字段,上部,该值指示是否它应该上部或下部组成。 现在,我们可以定义新的通道与请求/响应端口,将消息参数中返回给字符串消息:
channel Operator
{
input Argument Arg : String; // Argument in, String out
}
具有已定义此频道,变得相对简单,编写实现该信道,的图 6 所示的代理程序。 请注意此代理从技术上讲不退出其构造函数--它只是在循环中旋转、 阻止实例发送给它,一个参数之前在参数端口上接收电话,然后发送的 ToUpper ToLower 的结果 (根据的左上角,值当然) 在同一端口上备份。
图 6 代理执行通道
agent OperatingAgent : channel Operator
{
public OperatingAgent()
{
while (true)
{
var result = receive(PrimaryChannel::Arg);
if (result.RequestValue.upper)
result <-- result.RequestValue.arg.ToUpper();
else
result <-- result.RequestValue.arg.ToLower();
}
}
}
从调用方使用该 OperatingAgent 的 (用户) 角度是在 ProcessAgent 本身不不同:我们将创建使用内置的方法 CreateInNewDomain,它的一个实例,并启动参数绔 彛过帐参数实例。 我们做,但是,对象返回的最终将产生从该代理程序是只获取结果 (通过另一个 receive() 操作中) 的方法,如图 7 显示响应。
运行时, 它为预期--基于当前的毫秒,在发送该参数时,命令行字符串将是大写或小写。 并仍,所有没有任何直接的开发人员的线程交互。
所以很少但到目前为止
Axum 清楚地表示的考虑,尽管其明显的差异,从 C# 编程,和不同的方法,实现大量的最通用的编程语言的功能。 其主要目的,但是,并发和线程的友好的代码周围居中并且因此,用于最需要 (或从中受益) 的问题并发。
幸运的是,团队生成 Axum 未尝试 reinvent C# 语言。 因为 Axum 编译到.NET 程序集,就像其他.NET 语言那样,就相对简单,以生成应用程序中 Axum (一个类库项目,而不是一个控制台应用程序),并发的粗部分,然后从 C# 或 Visual Basic 在调用到其中。 Axum 分发附带一个执行此操作的示例 (餐饮哲学家示例,传统并发问题 WinForms 应用程序中的说明),和质量时间与反射器 Axum 编译库上将显示许多有关的互操作性内存占用量。 使用 Axum 生成库库可调用从另一种语言 (C# 或 Visual Basic) 可以进行大量的并发使用得更容易访问其他的.NET 技术,如 Web 或桌面应用程序向一个长方法转。
图 7 使用方法 CreateInNewDomain
agent ProcessAgent : channel Microsoft.Axum.Application
{
public ProcessAgent()
{
String [] args = receive(PrimaryChannel::CommandLine);
var opAgent = OperatingAgent.CreateInNewDomain();
var correlators = new IInteractionSource<String>[args.Length];
for (int i=0; i<args.Length; i++)
correlators[i] = opAgent::Arg <-- new Argument {
arg = args[i],
upper = ((System.DateTime.Now.Millisecond % 2) == 0) };
for (int i=0; i<correlators.Length; i++)
Console.WriteLine("Got {0} back for {1}",
receive(correlators[i]), args[i]);
PrimaryChannel::ExitCode <-- 0;
}
}
事实上,Axum 使用一个实验 C# 编译器提供了一些有趣且不同功能的 C# 3.0 (3.0 + 异步方法) 其中的任何暗示的 C# 4.0,顺便的超集。 这会允许,但是,是混合和匹配的 C# 和 Axum 代码,项目中,内容唯一 Axum 到目前为止。 查看 details.Axum Axum 程序员指南有其他功能不介绍,Axum 程序员指南,包括关闭关系与 WCF,以使其更易于编写高时间刻度的服务中的某些详细介绍,但本简介应该足以开始构建应用程序、 库或 Axum 中的服务。 请记住这是一个信息检索项目,用户可以提供通过 Axum 论坛 DevLabs 网站上的反馈。
如果 Axum 失败,则将转换成一个传送产品或读取器要办之前先使用它吗? 对于初学者的方式来说 Axum 的成功或失败取决显著部分用户的反应和以便将实时或质量反馈--通过清除发送您的想法 Axum 团队和其发布公用方式 agitate! 但即使 Axum 失败 graduate,请记住,Axum 概念不是唯一的。 在学校的 phraseology,Axum 体现演员模型的并发,并且多个其他演员模型可用于.NET 开发人员需要一些生产今天的。 打开源项目"Retlang"体现这些的概念的几个一样在 F # 语言 (查找在 F # MailboxProcessor 类型) 或 Microsoft 并发和控制运行库 (CCR),这两个的都是附近传送状态,如果没有已存在。
在结束,请记住,目标是不需要创建存在,使用每个基于 CLR 的语言的项目,但若要查找可以解决特定问题和要求这种情况时使用的语言。
并且请记住 linguas calles,到 homines vales。
Ted Neward 关联,预通过他讲述,写入和上构建可靠的企业系统 coaches Neward 与主体。 他的写入大量书籍,教给朗读,世界各地的会议是一个 INETA 扬声器和已收到该 MVP 奖励三个不同区域中的专业知识。 在 ted@tedneward.com 或通过在 blogs.tedneward.com 他的博客,请访问他。