Dies sind maschinell übersetzte Inhalte, die von den Mitgliedern der Community bearbeitet werden können. Sie können die Übersetzung verbessern, indem Sie auf den jeweils zum Satz gehörenden Link "Bearbeiten" klicken.Mithilfe des Dropdown-Steuerelements "Inhalt anzeigen" links oben auf der Seite können Sie zudem bestimmen, ob nur der englische Originaltext, nur die deutsche Übersetzung oder beides nebeneinander angezeigt werden.
The Polyglot Programmer
Concurrency with Channels, Domains and Messages
Ted Neward
In the last article, we looked at the programming language Cobra, an object-oriented language loosely derived from Python that embraces both static- and dynamic-typing, some "scripting" concepts similar to those found in Python and Ruby, and baked-in unit-testing features, among other things.
Upon deeper investigation, we saw that it had value and we were happy to have learned a new general-purpose programming language.
As interesting and powerful as general-purpose languages are, however, sometimes what's needed is not a hammer, saw or screwdriver, but a 3/16-inch nut driver; in other words, as much as we as developers value the tools that provide wide-ranging capability and facility, sometimes the right tool for the job is a very, very specific one.
In this piece, we'll focus not on a language that re-creates all of the Turing-complete constructs that we're used to, but a language that focuses on one particular area, seeking to do it well, in this case, concurrency.
Careful and thorough readers of this magazine will note that this concurrency meme is not an unfamiliar one to these pages.
The past few years have seen numerous articles on concurrency come through here, and MSDN Magazine is hardly unique in that respect -- the blogosphere, Twitterverse and developer Web portals are all rife with discussions of concurrency, including a few naysayers who think this whole concurrency thing is another attempt to create a problem out of thin air.
Some industry experts have even gone so far as to say (according to rumor, at a panel discussion at an OOPSLA conference a few years ago) that "concurrency is the dragon we must slay in the coming decade."
Some developers will be wondering what the problem really is -- after all, .NET has had multi-threading capability for years, via the asynchronous delegate invocation idiom (BeginInvoke/EndInvoke), and provides thread-safety mechanisms via the monitor construct (the lock keyword from C#, for example) and other explicit concurrency object constructs (Mutex, Event, and so on, from System.Threading).
So all this new fuss seems like making a mountain out of a molehill.
Those developers would be absolutely right, assuming they write code that is (1) 100 percent thread-safe, and (2) taking advantage of every opportunity for task or data parallelism in their code by spinning up threads or borrowing threads from thread pools to maximize the use of all the cores provided by the underlying hardware.
(If you're safe on both counts, please move on to the next article in the magazine.
Better yet, write one and tell us your secrets.)
Numerous proposals to solve, or at least mitigate, this problem have been floated through the Microsoft universe, including the Task Parallel Library (TPL), Parallel FX extensions (PFX), F# asynchronous workflows, the Concurrency and Control Runtime (CCR), and so on.
In each of these cases, the solution is either to extend a host language (usually C#) in ways to provide additional parallelism or to provide a library that can be called from a .NET general-purpose language.
The drawback to most of these approaches, however, is that because they depend on the semantics of the host language, doing the right thing, from a concurrency perspective, is an elective choice, something developers have to explicitly buy into and code appropriately.
This unfortunately means that the opportunity for doing the wrong thing, whatever that may be, thus creates a concurrency hole that code can ultimately fall into and create a bug waiting to be discovered, and this is a bad thing.
Developer tools shouldn't, in the ideal, allow developers to do the wrong thing -- this is why the .NET platform moved to a garbage-collected approach, for example.
Instead, developer tools should cause developers to, as Rico Mariani (Microsoft Visual Studio architect and former CLR Performance architect) puts it, "fall into the pit of success" -- what developers find the easiest to do should be the right thing, and what developers find hard to do or find impossible to do should be the wrong thing.
Toward that end, Microsoft has released as an incubation project a new language aimed squarely at the concurrency domain, called "Axum." In the spirit of enabling developers to "fall into the pit of success," Axum is not a general-purpose language like C# or Visual Basic, but one aimed squarely at the problem of concurrency, designed from the outset to be part of a suite of languages that collectively cooperate to solve a business problem.
In essence, Axum is a great example of a domain-specific language, a language designed specifically to solve just one vertical aspect of a problem.
As of this writing, Axum is labeled as version 0.2, and the details of Axum's language and operation are entirely subject to change, as stated by the disclaimer at the front of the article.
But between "as I write this" and "as you read this," the core concepts should remain fairly stable.
In fact, these concepts are not unique to Axum and are found in a number of other languages and libraries (including several of the aforementioned projects, such as F# and the CCR), so even if Axum itself doesn't make it into widespread use, the ideas here are invaluable for thinking about concurrency.
What's more, the general idea -- a language specific to the problem domain -- is quickly growing into a "big thing" and is worth examining in its own right, albeit in a later piece.
Beginnings
The Axum bits for download currently live on the Microsoft Labs Web site at msdn.microsoft.com/en-us/devlabs/dd795202.aspx; be sure to pick up at least both the .msi (either for Visual Studio 2008 or for Beta 1 of Visual Studio 2010) and the Programmer's Guide.
Once you install these, fire up Visual Studio, and create a new Axum Console Application project, as shown in Figure 1.
Figure 1 Axum Console Application Project
Replace it with the code shown in Figure 2.
This is the Axum "Hello World," which serves the same purpose as every other Hello World application does: to verify that the installation worked and to get a basic sense of the syntax.
Assuming everything compiles and runs, we're good to go.
Figure 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;
}
}
}
}
As you can see, the syntax of Axum is highly reminiscent of C#, putting Axum loosely into the category of languages known as the C family of languages: it uses curly braces to denote scope, semicolons to terminate statements, and so on.
With that said, however, there are obviously a few new keywords to get to know (domain, agent, channel, receive, just for starters), plus a few new operators.
Formally, Axum is not really a derivative of C#, but a selective subset with added new features.
Axum includes all C# 3.0 statement types (such as lambdas and inferred local variables) and expressions (such as algebraic calculations and yield return/yield break), methods, field declarations, delegates, and enumeration types, but it leaves out classes, interfaces, or struct/value type definitions, operator declarations, properties, const fields/locals, and static fields/methods.
Hear that whooshing sound?
That's the wind rushing past your ears, and if you feel a slight fluttering in your stomach, it's OK.
Jumping off cliffs is a good thing.
Concepts and Syntax
The Axum syntax directly reflects the core concepts of Axum, just as it does for C#.
Where C# has classes, however, Axum has agents.
In Axum, an agent is an entity of code, but unlike a traditional object, agents never directly interact -- instead, they pass messages to one another in an asynchronous fashion through channels, or to be more accurate, across the ports defined on the channel.
Sending a message on a port is an asynchronous operation -- the send immediately returns -- and receiving a message on a port blocks until a message is available.
This way, threads never interact on the same data elements at the same time, and a major source of deadlock and inconsistency is effectively lifted away.
To see this in action, consider the Hello code.
The agent MainAgent implements a special channel, called Application, which is a channel the Axum runtime understands directly -- it will process any incoming command line arguments and pass them over the Application's PrimaryChannel channel, on the port CommandLines.
When constructed by the runtime, the MainAgent uses the receive operation (an Axum primitive) to block that port until those arguments are available, at which point execution in the MainAgent constructor continues.
The runtime, having done its job, then blocks on the ExitCode port of the PrimaryChannel, which is the signal that the application is finished.
Meanwhile, the MainAgent rolls through the array, printing each one, and once finished, sends the integer literal 0 to the ExitCode port.
This is received by the runtime, which then terminates the process.
Notice how Axum, from the very beginning, is taking this concurrent execution idea seriously -- not only are developers sheltered from the creation and manipulation of threads or concurrency/locking objects, but the Axum language assumes a concurrent approach even for something as simple as Hello.
For something as simple as Hello, arguably, this is a waste of time -- however, most of us don't write applications as simple as Hello very often, and those who do, can always fall back to the traditional object-oriented single-threaded-by-default application language (C# or Visual Basic or whatever else strikes your fancy).
Figure 3 Process Application
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);
}
}
}
}
Saying Hello Is for Wimps
A slightly more interesting example is the Process application, as Figure 3 shows.
In this version, we see a new Axum concept, the function.
Functions are different from traditional methods in that the Axum compiler guarantees (by way of compiler error) that the method never modifies the agent's internal state; in other words, functions may not have side effects.
Preventing side effects adds to Axum's usefulness as a concurrency-friendly language -- without side effects, it becomes extremely difficult to accidentally write code where a thread modifies shared state (and thus introduces inconsistency or incorrect results).
But we're still not done here.
Axum not only wants to make it easier to write thread-safe code, it also wants to make it easier to write thread-friendly code.
With two- and four-core machines becoming more mainstream, and 8- and 16-core machines visible on the horizon, it's important to look for opportunities to perform operations in parallel.
Axum makes this easier, again by lifting the conversation away from threads and providing some higher-level constructs to work with.
Figure 4 shows a modified version of the ProcessAgent.
Two new things are happening here: one, we've created an interaction point, which can serve as a source or a recipient of messages (in this case, it will be both); and two, we've used the forwarding operator to create a pipeline of messages from the interaction point to the ProcessArgument function.
By doing this, any message sent to the "arguments" interaction point will be forwarded to ProcessArgument for , well , processing.
From there, all that's left is to iterate through the command-line arguments and send each one as a message (via the send operator, the <-- operation inside the loop) to the arguments interaction point, and the work begins.
(Axum formally defines this as a data flow network, and there's much it can do in addition to creating a simple 1-to-1 pipeline, but that's beyond the scope of this introduction.
The Programmer's Guide has more details for those who are interested.)
Figure 4 Modified Version of the 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);
}
}
}
}
By now putting the data through the pipeline, Axum can decide how and when to spin up threads to do the processing.
In this simple example, it may not be useful or efficient to do so, but for more sophisticated scenarios, it quite often will.
Because the arguments interaction point is an ordered interaction point, the messages sent down the pipeline -- and, more important, the results returned -- will preserve their place in the ordering, even if each message is processed on a separate thread.
What's more, this concept of pipelining messages from one interaction point to another is a powerful concept that Axum borrows from functional languages like F#.
Figure 5 shows how easy it is to add some additional processing into the pipeline.
Notice that, again, the function UpperCaseIt is not modifying internal ProcessAgent state but is operating only on the object handed in.
Additionally, the pipeline is modified to include UpperCaseIt before the ProcessArgument function, and the intuitive thing happens -- the results of UpperCaseIt are passed into the ProcessArgument function.
As a mental exercise, consider how many lines of C# code would be required to do this, bearing in mind that all of this is also being done across multiple threads, not just a single thread of execution.
Now imagine debugging the code to make sure it executes correctly.
(Actually, imagining it isn't necessary -- use a tool like ILDasm or Reflector to examine the generated IL.
It's definitely not trivial.)
By the way, I have a small confession to make -- as written, my sample doesn't execute correctly.
When the preceding code is run, the application returns without displaying anything.
This isn't a bug in the Axum bits; this is as intended behavior, and highlights how programming in a concurrent mindset requires a mental shift.
When the arguments interaction point is receiving the command-line arguments via the send operator (<--), those sends are done asynchronously.
The ProcessAgent is not blocking when it sends those messages, and therefore if the pipeline is sufficiently complex, the arguments are all sent, the loop terminates, and the ExitCode is sent, terminating the application, all before anything can reach the console.
To fix this, ProcessAgent needs to block until the pipeline has completed operation; it needs to hold the thread alive with something like a Console.ReadLine().
(This turns out to be tricky in practice -- see the Axum team blog for the details.) Or we need to change the way ProcessAgent works, and it's this latter course I'm going to take, primarily in order to demonstrate a few more of Axum's features.
Instead of doing the work inside itself, ProcessAgent will defer the work to a new agent.
But now, just to make things a bit more interesting, this new agent will also want to know whether the argument should be uppercase or lowercase.
To do this, the new agent will need to define a more complex message, one that takes not only the string to operate on, but also a Boolean value (true for uppercase).
To do that, Axum requires that we define a schema.
Figure 5 Pipelining Messages
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);
}
}
}
}
Process my Argument
Implicitly until this point, one of the goals of Axum has been to isolate different executing entities (agents) away from one another, and it has done so by making copies of the messages and handing those to the agent, rather than passing objects directly.
No shared state -- even through parameters -- means no accidental chances of thread conflict.
While that works fine for types defined in the .NET Base Class Library, it can easily be defeated if .NET programmers don't do the right thing in user-defined types.
To combat this, Axum requires that a new kind of message be defined as a schema type -- essentially an object without the methods and with required and/or optional fields, using the schema keyword:
schema Argument
{
required String arg;
optional bool upper;
}
This defines a new data type, Argument, which consists of a required field, arg, containing the string that is to be made uppercase or lowercase, and an optional field, upper, indicating whether it should be made upper or lower.
Now, we can define a new channel with a request/response port that takes Argument messages in and gives String messages back:
channel Operator
{
input Argument Arg : String; // Argument in, String out
}
Having defined this channel, it becomes relatively trivial to write the agent that implements the channel, as shown in Figure 6.
Notice that this agent technically never exits its constructor -- it simply spins in a loop, calling receive on the Arg port, blocking until an Argument instance is sent to it, and then sending the result of the ToUpper or ToLower back on the same port (depending on the value of upper, of course).
Figure 6 Agent Implementing Channel
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();
}
}
}
From the callers' (users') perspective, using the OperatingAgent is no different than the ProcessAgent itself: we create an instance of it using the built-in method CreateInNewDomain, and start posting Argument instances to the Arg port.
When we do, however, an object is returned that will eventually yield the response back from the agent, which is the only way to harvest the results (via another receive() operation), as Figure 7 shows.
When run, it does as expected -- based on the current millisecond at the time of the Argument sent, the command-line string will either be uppercase or lowercase.
And, still, all without any direct thread interaction on the part of the developer.
So Little, yet So Far
Axum clearly represents a different way of thinking about programming, and despite its stark differences from C#, implements a significant number of features found in most general-purpose programming languages.
Its main purpose, however, centers around concurrency and thread-friendly code, and as such, works best with problems that require (or benefit from) concurrency.
Fortunately, the team building Axum didn't try to reinvent the C# language.
Because Axum compiles to .NET assemblies just as other .NET languages do, it's relatively trivial to build the concurrent-heavy parts of the application in Axum (as a Class Library project, instead of a Console Application), and then call into it from C# or Visual Basic.
The Axum distribution ships with a sample that does this (the Dining Philosophers sample, illustrating the classic concurrency problem in a WinForms application), and quality time spent with Reflector over the Axum-compiled libraries will reveal a great deal about that interoperability footprint.
Using Axum to build library libraries callable from another language (C# or Visual Basic) can go a long way toward making heavy concurrency use vastly more accessible to the other .NET technologies, such as Web or desktop applications.
Figure 7 Using Method 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;
}
}
In fact, Axum uses an experimental C# compiler that provides some interesting and different features, a superset of C# 3.0 (3.0 + asynchronous methods), none of which is implied for C# 4.0, by the way.
What this does permit, however, is the ability to mix-and-match C# and Axum code in the same project, something unique to Axum so far.
See the Axum Programmer's Guide for details.Axum has additional features not described here, covered in some detail in the Axum Programmer's Guide, including a close relationship with WCF to make it easier to write high-scale services, but this introduction should be sufficient to get started building applications, libraries or services in Axum.
Remember that this is a research project, and users can offer feedback via the Axum Forums on the DevLabs Web site.
What if Axum fails to turn into a shipping product, or readers want to use it before then?
For starters, Axum's success or failure depends significantly in part on user reaction, and so will live or die by quality feedback -- send the Axum team your thoughts, and agitate for its release in a public fashion!
But even if Axum fails to graduate, remember, the concepts behind Axum are not unique.
In the academic phraseology, Axum embodies the Actors model of concurrency, and several other Actors models are available for the .NET developer who wants something for production today.
The open-source project "Retlang" embodies several of these concepts, as does the F# language (look at F#'s MailboxProcessor type) or the Microsoft Concurrency and Control Runtime (CCR), both of which are near-shipping status, if not there already.
In the end, remember, the goal is not to create projects that use every CLR-based language in existence, but to find languages that can solve particular problems and use them when the situation demands.
And remember, Quot linguas calles, tot homines vales.
Ted Neward
is the principal with Neward and Associates, through which he speaks, writes and coaches on building reliable enterprise systems.
He's written numerous books, taught and spoken at conferences all over the world, is an INETA speaker, and has received the MVP award in three different areas of expertise.
Reach him at ted@tedneward.com or through his blog at blogs.tedneward.com.
|
Der polyglotte Programmierer
Parallelität mit Kanälen, Domänen und Nachrichten
Ted Neward
Im letzten Artikel wir in der Programmiersprache Cobra eine objektorientierte Sprache, Python, die beide statische und dynamische-einzugeben, bevorzugt lose abgeleitet suchten einige "Skripterstellung"Konzepte ähnlich, die im Python und Ruby und integrierte Einheit testen Features, unter anderem gefunden.
Nach dem tiefer Untersuchung haben wir leider gerne eine neue allgemeine Programmiersprache gelernt haben, hat Wert gesehen.
Als interessante und allgemeine Sprachen sind jedoch leistungsstarke was benötigt wird ist manchmal nicht Hammer, Säge oder Schraubendreher, aber einen Treiber 3/16 Zoll - Flügelmutter;mit anderen Worten, als wir als Entwickler die Tools, die weitreichende Funktion und Möglichkeit bieten Wert, ist manchmal des richtigen Tools für die Auftrag sehr, sehr spezifische.
In diesem Stück konzentrieren wir uns nicht auf eine Sprache, die alle die Konstrukte Turing abgeschlossen neu erstellt, die wir verwendet, aber eine Sprache, die sich auf einen bestimmten Bereich konzentriert dafür auch Suchvorgänge in diesem Fall Parallelität.
Sorgfältige und gründliche Leser dieses Magazins werden beachten, dass dieser Parallelität Meme nicht eine auf diese Seiten nicht vertraut ist.
Die letzten Jahren haben gesehen, zahlreiche Artikel zur Parallelität über kommen hier und MSDN Magazin ist kaum eindeutig in dieser Hinsicht--Blogosphere, Twitterverse und Entwickler Webportale sind alle voller Diskussionen der Parallelität, darunter ein paar Naysayers, die denken, dieses gesamte Parallelität Ding anderen Versuch dass, ein Problem liegt außerhalb des dünner Luft zu erstellen ist.
Einige Branchenexperten haben sogar verschwunden bisher als angenommen (entsprechend zu böse bei einer Podiumsdiskussion an einer Konferenz OOPSLA vor wenigen Jahren), "Parallelität ist die wir im nächsten Jahrzehnt slay müssen Dragon."
Einige Entwickler werden Fragen sich wie das Problem wirklich ist – nachdem alle .NET hat Multithreading Funktion für Jahre über asynchrone Delegaten Aufruf Ausdrucksweise (BeginInvoke/EndInvoke) und bietet Threadsicherheit Mechanismen, über das Monitor-Konstrukt (das Lock-Schlüsselwort von C#-Beispiel) und andere explizite Parallelität Objekt Konstrukte (Mutex, Ereignis und usw. von System.Threading).
Damit diese neue Fuss ein Berg aus einem Molehill vornehmen scheint.
Die Entwickler wäre absolut richtig, vorausgesetzt Sie Code schreiben, (1) 100 Prozent threadsicher und jede Verkaufschance für Aufgabe oder Daten Parallelität in Ihren Code (2) durch Nutzung durch Threads drehende oder ausleiht Threads von Threadpools zum Optimieren der Verwendung von alle Kerne, die von der zugrunde liegenden Hardware bereitgestellt.
(Wenn Sie auf beide Zähler, Bitte fahren Sie mit im nächsten Artikel im Magazin.
Besser noch, Schreiben Sie eine und Teilen Sie uns Ihre vertraulichen Daten.)
Zahlreiche Vorschläge zu lösen oder zumindest zu verringern, dieses Problem sind über Microsoft-Universum, einschließlich der Task Parallel Library (TPL), Parallel FX-Erweiterungen (PFX), f# asynchronen Workflows, die Parallelität und der Runtime (CCR) und usw. unverankert wurde.
In allen diesen Fällen ist die Lösung, erweitern eine Host-Sprache (normalerweise c#) Möglichkeiten, um zusätzliche Parallelität oder eine Bibliothek bereitstellen, die aus einer allgemeinen .NET-Sprache aufgerufen werden kann.
Der Nachteil für die meisten dieser Ansätze ist jedoch, da Sie die Semantik der Host Language abhängig sind, auf diese Weise der Sache rechten im Hinblick auf die Parallelität eine Fakultative Wahl, etwas Entwickler ist explizit in kaufen und den code entsprechend.
Dies leider bedeutet, die möglicherweise die Möglichkeit, damit der falschen Sache, was daher erstellt ein Loch Parallelität, dass Code letztendlich in fallen und erstellen ein Fehlers darauf warten, erkannt werden kann und dies etwas Schlechtes ist.
Developer Tools sollten nicht, in dem Ideal Entwicklern ermöglichen das falsche tun – deshalb .NET Plattform z. B. ein Ansatz Garbage Collection verschoben.
Developer Tools sollte stattdessen Entwickler zu verursachen, wie Rico Mariani (Microsoft Visual Studio-Architekten und ehemaligen von CLR-Architekt) es "fallen in den Pit Erfolg" versetzt--Was Entwickler finden Sie am einfachsten dazu sollte die richtige Sache und welche Entwickler finden schwer oder unmöglich ist, führen Sie finden das falsche Element sollte.
Richtung aus diesem Grund hat Microsoft als Incubation Projekt veröffentlicht eine neue Sprache auf die Parallelität Domäne namens "Axum". squarely gerichtet In den Teamgeist aktivieren Entwickler "in den Pit Erfolg, fallen"Axum ist keine allgemeine Sprache wie c# oder Visual Basic, aber eine das Problem der Parallelität, entworfen von Anfang an Teil einer Reihe von Sprachen, die zusammenfassend, zusammenarbeiten um ein Unternehmensproblem zu lösen squarely abzielt.
Axum ist im Wesentlichen ein gutes Beispiel einer domänenspezifischen Sprache eine Sprache speziell für die nur einen vertikalen Aspekt ein Problem zu lösen.
Wie diese schreiben, Axum als Version 0.2 gekennzeichnet ist und die Details des Axum Sprache und Vorgang vollständig geändert werden, wie durch die Verzichtserklärung am Anfang des Artikels angegeben.
Aber zwischen "Wie ich dies schreibe"und "wie Sie dies lesen"die grundlegenden Konzepte sollte einigermaßen stabil bleiben.
In der Tat diese Konzepte sind nicht eindeutig, Axum und befinden sich in einer Reihe von anderen Sprachen und Bibliotheken (einschließlich verschiedener die oben genannten Projekte, z. B. f# und die CCR), so auch wenn Axum selbst in weit verbreiteten Verwendung erleichtern nicht, hier die Ideen für Parallelität denken unschätzbare Hilfe.
Was ist mehr wachsende der Grundgedanke--eine Sprache spezifisch für die Problemdomäne--schnell in eine "große Sache"und lohnt sich in einem eigenen rechten untersuchen Wenn auch in späteren Stück.
Anfang
Die Axum-Bits für den Download live derzeit auf der Microsoft-Labs-Website unter msdn.microsoft.com/devlabs/dd795202.aspx; müssen Sie unbedingt zumindest sowohl der MSI-(entweder für Visual Studio 2008 oder Betaversion 1 von Visual Studio 2010) als auch im Programmierhandbuch abholen.
Nach der Installation diese Visual Studio starten Sie und erstellen Sie ein neues Axum Konsolenanwendungsprojekt, wie in Abbildung 1 dargestellt.
Abbildung 1 Axum Console Application Project
Durch den Code in Abbildung 2 ersetzen.
Dies ist die Axum "Hello World"führt die dient demselben Zweck wie jede andere Hello World-Anwendung: Stellen Sie sicher, dass die Installation gearbeitet und eine grundlegende Vorstellung von der Syntax zu erhalten.
Vorausgesetzt, dass alles kompiliert und ausgeführt wird, sind wir gut zu wechseln.
Abbildung 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;
}
}
}
}
Wie Sie sehen können, ist die Syntax des Axum sehr erinnert von c#, lose einfügen Axum in der Kategorie der Sprachen, die Produktfamilie der Sprachen C genannt: geschweifte Klammern verwendet, um kennzeichnen Bereich Semikolons Anweisungen zu beenden und so weiter.
Mit dem gesagt gibt es jedoch offensichtlich einige neue Schlüsselwörter zu kennen, sowie einige neue Operatoren (Domäne, Agent, Channel, empfangen, zunächst einmal nur).
Formal, ist Axum nicht wirklich eine Ableitung von c#, aber eine ausgewählte Teilmenge mit neuen Features hinzugefügt.
Axum enthält, alle c# 3.0-Anweisung Typen (z. B. Lambdas und hergeleiteten lokale Variablen) und Ausdrücke (wie z. B. algebraischer Berechnungen und „ Yield Return-Yield Break), Methoden, Felddeklarationen, Delegaten und Enumerationstypen, aber Sie verlässt, Klassen, Schnittstellen oder Struct-Wert-Typdefinitionen Operatordeklarationen, Eigenschaften, const Felder/lokale und statische Felder/Methoden.
Hören die whooshing Sound zu?
Ist der Wind hinter Ihrem Ohren rushing, und wenn Sie eine leichte fluttering in Ihre ungutes, OK.
Springen aus Cliffs ist eine gute Sache.
Konzepte und Syntax
Die Syntax die Axum entspricht direkt die grundlegenden Konzepte der Axum, ebenso wie für c#.
Bei C#-Klassen besitzt, verfügt Axum jedoch Agents.
In Axum ein Agent ist eine Entität von Code, aber im Gegensatz zu herkömmlichen-Objekt Agents nie direkt interagieren--stattdessen übergeben Sie Nachrichten zu anderen in eine asynchrone Weise über Channels oder genauer, über die Ports auf dem Kanal definiert werden.
Senden einer Nachricht auf einem Anschluss ist eine asynchrone Operation--das Senden sofort gibt-- und Empfangen einer Nachricht auf einen Port blockiert bis eine Meldung ist verfügbar.
Auf diese Weise Threads nie interagieren auf den gleichen Datenelementen zur gleichen Zeit, und eine wichtige Quelle von Deadlocks und Inkonsistenzen ist effektiv sofort transformiert.
Um diese Aktion anzuzeigen, sehen Sie sich den Hello-Code.
Der Agent MainAgent implementiert einen speziellen Kanal Anwendung ein Channel, den die Runtime Axum direkt ist--versteht wird alle eingehenden Befehlszeilenargumente verarbeiten und übergeben Sie über die Anwendung PrimaryChannel Kanal auf dem Port CommandLines, aufgerufen.
Wenn von der Laufzeit erstellt, der MainAgent verwendet den Empfangsvorgang (ein Axum-Primitiv), um den Anschluss blockiert, bis diese Argumente zur Verfügung stehen, an welcher Stelle die Ausführung im Konstruktor MainAgent wird fortgesetzt.
Der Runtime müssen erfolgt die Auftrag und Blöcke auf dem Port ExitCode der PrimaryChannel, das Signal, dass die Anwendung beendet wurde.
In der Zwischenzeit sendet die Filmrollen MainAgent über das Array, jeweils, Drucken und nach dem Beenden der Integer-literal 0 an den ExitCode Port.
Dies wird durch die Runtime empfangen, die dann den Vorgang beendet.
Beachten Sie, wie Axum, von Anfang, diese Idee gleichzeitige Ausführung stark beansprucht--nicht nur sind Entwickler betriebliche von der Erstellung und Manipulation von Threads oder Objekte Parallelität/sperren, sondern die Axum-Sprache wird davon ausgegangen eine gleichzeitige Methode auch für etwas so Einfaches wie Hello.
Etwas so Einfaches wie Hello wohl, dies ist Zeitverschwendung--jedoch die meisten von uns nicht so einfach wie Hello Anwendungen schreiben, sehr häufig und Personen zu tun, können immer zurückgreifen auf die herkömmliche objektorientierte einzelne-threaded-durch-Standardanwendung Sprache (C#- oder Visual Basic oder beliebige andere Ihre ausgefallen befinden).
Abbildung 3 Process-Anwendung
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);
}
}
}
}
Sagen Hello ist für Wimps
Ein etwas Interessantes Beispiel ist der Process-Anwendung, wie Abbildung 3.
In dieser Version sehen wir ein neues Axum-Konzept der Funktion.
Funktionen unterscheiden sich von traditionellen Methoden, der Axum-Compiler (mithilfe von Compilerfehler) garantiert, dass die Methode nie internen Status des Agents; ändertmit anderen Worten, können Funktionen keine Nebeneffekte haben.
Nebeneffekte verhindern hinzugefügt Axum des Nützlichkeit als Parallelität angezeigten Sprache--ohne Nebeneffekte, es extrem schwer zu versehentlich schreiben Sie Code wird, wobei ein Thread gemeinsam genutzten Zustand ändert (und daher führt Inkonsistenz oder falsche Ergebnisse).
Aber wir sind noch nicht erfolgt hier.
Axum möchte nicht nur um threadsicheren Code schreiben, auch leichter Thread angezeigten Code schreiben möchte zu erleichtern.
Mit zwei und vier-Kern-Computern mehr alltäglich und 8 und 16-Core-Computer auf dem Horizont sichtbar ist es wichtig nach Möglichkeiten, Vorgänge parallel auszuführen.
Axum vereinfacht, erneut durch die Unterhaltung verlassen von Threads absetzen und einige Konstrukte höherer Ebene zum Arbeiten mit bereitstellen.
Abbildung 4 zeigt eine geänderte Version der ProcessAgent.
Hier sind zwei neue Dinge geschieht: eine, erstellten wir eine Interaktion Punkt als eine Datenquelle oder einen Empfänger der Nachrichten verwendet werden kann (in diesem Fall es wird, beide);und zwei, wir haben den Weiterleitung Operator verwendet, um eine Rohrleitung von Nachrichten von dem Punkt Interaktion der ProcessArgument-Funktion zu erstellen.
Auf diese Weise, um "Argumente" gesendetInteraktion Punkt werden an ProcessArgument für gut, weitergeleitet verarbeiten.
Von dort alle Links des besteht darin, die Befehlszeilenargumente durchlaufen und jeweils als Nachricht senden (über den Send-Operator, der <--Vorgang innerhalb der Schleife) Argumente Interaktion Punkt und die Arbeit beginnt.
(Axum formal definiert dies als Daten-Fluss Netzwerk, und es gibt viel es darüber hinaus kann zum Erstellen einer einfachen 1: 1-Pipeline, aber das Gegenstand dieser Einführung ist.
Das Programmierhandbuch hat weitere Details für diejenigen, die interessiert sind.)
Abbildung 4 geänderte Version des 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);
}
}
}
}
Mittlerweile versetzen die Daten über die Pipeline Axum kann wie und wann entscheiden, Threads für die Verarbeitung des Drehfelds.
In diesem einfachen Beispiel möglicherweise nicht sinnvoll oder zu diesem Zweck effiziente allerdings für komplexere Szenarios wird es häufig.
Da der Argumente Interaktion Punkt eine geordnete Interaktion Punkt entlang der Pipeline--gesendeten Nachrichten ist und wichtiger, die Ergebnisse zurückgegeben--werden beibehalten ihrer Position in der Reihenfolge, selbst wenn jede Nachricht in einem separaten Thread verarbeitet wird.
Dieses Konzept der Nachrichten von einem Interaktion Punkt pipelining ist darüber ein leistungsfähiges Konzept, das funktionale Sprachen wie f# Axum Anlehnungen.
Abbildung 5 zeigt, wie einfach einige zusätzliche Verarbeitung in die Pipeline hinzugefügt werden kann.
Beachten Sie, erneut, die Funktion UpperCaseIt internen ProcessAgent-Status nicht ändern ist aber nur auf das Objekt arbeitet übergeben.
Darüber hinaus wird die Pipeline geändert, um vor der Funktion ProcessArgument UpperCaseIt enthalten und das intuitive, was geschieht – werden die Ergebnisse der UpperCaseIt in ProcessArgument-Funktion übergeben.
Eine Übung Ihre sollten Sie wie viele Zeilen von C#-Code müssen würden Sie dazu mit Denken Sie daran, dass all dies auch über mehrere Threads nicht nur einen einzigen Ausführungsthread durchgeführt wird.
Stellen Sie sich jetzt vor Debuggen des Codes sicherstellen, dass er ordnungsgemäß ausgeführt wird.
(Tatsächlich imagining es ist nicht notwendig--verwenden ein Tool wie ILDasm oder Reflector generierte IL untersuchen.
Es ist sicherlich nicht trivial.)
Die Art und Weise, habe ich eine kleine Confession zu--geschrieben, nicht korrekt mein Beispiel ausführen.
Wenn der vorhergehende Code ausgeführt wird, gibt die Anwendung ohne nichts zurück.
Dies ist ein Fehler in der Axum-Bits; nichtDies wird als beabsichtigte Verhalten und hebt hervor, wie in einer gleichzeitigen Denkweise Ihre UMSCHALT Programmieraufwand.
Der Argumente Interaktion Punkt erhält beim die Befehlszeilenargumente über die Send-Operator (<--), diese sendet asynchron erfolgen.
Die ProcessAgent ist nicht blockiert, wenn er diese Nachrichten sendet und daher ist die Pipeline ausreichend komplex, die Argumente alle gesendet, werden die Schleife beendet, und der ExitCode gesendet wird, beendet die Anwendung alle bevor irgendetwas die Konsole erreichen kann.
Um dieses Problem zu beheben, muss ProcessAgent blockiert, bis die Pipeline Vorgang abgeschlossen istEr muss den Thread mit etwa einem Console.ReadLine() aktiv halten.
(Dies erweist sich schwierig sein, in der Praxis werden – finden Sie unter den Teamblog Axum den Details.) Oder wir müssen die Funktionsweise von ProcessAgent ändern und ganz letztere Kurs werde ausführen, hauptsächlich um ein paar weitere Axum des Features zu demonstrieren.
Statt die Arbeit in sich selbst zu tun, wird ProcessAgent die Arbeit einen neuen Agenten verzögern.
Aber jetzt, nur um die Dinge etwas interessanter gestalten, dieser neue Agent werden auch möchten wissen, ob das Argument Groß- oder Kleinbuchstaben sein sollte.
Dazu müssen der neue Agent Definieren komplexer Nachricht, eine, die nicht nur die Zeichenfolge zum Verarbeiten hat, sondern auch einen booleschen Wert (True für Großbuchstaben).
Dies tun, muss bei Axum definieren wir ein Schema.
Abbildung 5 Pipelining Nachrichten
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);
}
}
}
}
Mein Argument verarbeiten
Implizit bis zu diesem Zeitpunkt eines der Ziele der Axum wurde, verschiedene ausgeführte Entitäten (Agents) von einem anderen zu isolieren, und es hat geschehen Kopien der Nachrichten und übergeben diese an den Agent, anstatt direkt übergeben von Objekten.
Keine freigegebenen Status--auch über Parameter--bedeutet, dass keine unbeabsichtigten Erfolgschancen Thread-Konflikt.
Während, problemlos in die Basisklassenbibliothek von .NET definierten Typen funktioniert, kann er einfach sein, wenn .NET Programmierer das richtige tun in benutzerdefinierten Typen nicht.
Um dies zu bekämpfen, erfordert die Axum, eine neue Art von Nachricht als einen Schematyp – im Wesentlichen ein Objekt ohne die Methoden und Felder erforderlich und/oder optional mit dem Schema-Schlüsselwort definiert werden:
schema Argument
{
required String arg;
optional bool upper;
}
Diese definiert einen neuen Datentyp, Argument, ein erforderliches Feld, Arg besteht, mit der Zeichenfolge, die Groß- oder Kleinbuchstaben, vorgenommen werden und ein optionales Feld, oberen, der angibt, ob Sie Ober- oder Untergrenze erfolgen soll.
Jetzt können wir einen neuen Kanal mit einem Anforderung-Antwort-Anschluss definieren, die nimmt Argument Nachrichten in und bietet String Nachrichten zurück:
channel Operator
{
input Argument Arg : String; // Argument in, String out
}
Dieser Kanal definieren, wird es relativ einfach, den Agent zu schreiben, der den Kanal, implementiert wie in Abbildung 6 dargestellt.
Bemerken Sie, dass dieser Agent technisch nie seinen Konstruktor beendet--es einfach in einer Schleife rotiert, Aufrufen empfangen auf dem Port Argument blockieren, bis ein Argument Instanz an Sie gesendet wird und senden das Ergebnis der ToUpper oder ToLower Sichern auf denselben Port (je des Wertes der oberen, des Kurses).
Abbildung 6 Agent implementieren Channel
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();
}
}
}
Von der Aufrufern(Benutzer) im Hinblick auf Verwenden der OperatingAgent unterscheidet sich nicht als ProcessAgent selbst: Wir erstellen eine Instanz mit die integrierte Methode CreateInNewDomain, und starten Argument Instanzen mit dem Argument Anschluss buchen.
Wenn wir, jedoch tun ist ein Objekt zurückgegeben, die schließlich die Antwort wieder aus der Agent liefert die ist die einzige Möglichkeit zum Sammeln der Ergebnisse (über einen anderen receive() Vorgang), wie Abbildung 7 zeigt.
Wenn ausführen, wird wird wie erwartet – basierend auf aktuellen Millisekunde bei der das Argument gesendet, die Befehlszeile-Zeichenfolge entweder Groß- oder Kleinbuchstaben.
Und noch alle ohne direkte Thread Interaktion seitens des Entwicklers.
Daher kleiner noch bisher
Axum klar gibt an, eine andere Weise Nachdenken über Programmierung und Trotz seiner starkem Unterschiede von c#, eine große Anzahl von Features in den meisten allgemeinen Programmiersprachen implementiert.
Jedoch seine Hauptaufgabe um Parallelität und Threads angezeigten Code zentriert und als solche Probleme, die erforderlich ist (oder profitieren) funktioniert am besten Parallelität.
Glücklicherweise hat das Team Axum erstellen versuchen Sie, die C#-Sprache Neuerfindung.
Da Axum .NET Assemblys, kompiliert genau wie andere .NET Sprachen, ist es relativ einfach die gleichzeitige Auslastung Teile der Anwendung in Axum (als ein Klassenbibliotheksprojekt, anstatt eine Konsolenanwendung) zu erstellen und es dann in c# oder Visual Basic aufruft.
Die Verteilung Axum im Lieferumfang von einer Stichprobe, die diese (Trinken Philosophen Beispiel zur Veranschaulichung des klassische Parallelität Problems in einer WinForms-Anwendung), und Qualität Zeitaufwand mit Reflector über die Axum-kompilierte Bibliotheken wird viel über die Interoperabilität Speicherbedarf.
Mithilfe von Axum Bibliothek Bibliotheken aufrufbare aus einer anderen Sprache (C#- oder Visual Basic) erstellen, kann eine lange Möglichkeit in Richtung hoher Parallelität verwenden für die andere .NET-Technologien, wie Web oder desktop-Anwendungen erheblich mehr zugänglich machen wechseln.
Abbildung 7 Methode CreateInNewDomain verwenden
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 verwendet einen experimentellen C#-Compiler, der einige interessante und anderen Features bietet eine Obermenge von c# 3.0 (3.0 + asynchrone Methoden), der die Art und Weise für c# 4.0, impliziert wird.
Was dies zulässt, ist jedoch die Möglichkeit, mischen und Übereinstimmung C#- und Axum-Code im selben Projekt Axum bisher etwas eindeutig.
Angezeigt im Axum Programmer's Guide für details.Axum zusätzliche hier nicht beschriebenen Features behandelt einige Details in der Axum Programmer's Guide, einschließlich eine enge Beziehung mit WCF, um Dienste hoch Skalierung, Schreiben erleichtern sollte diese Einführung jedoch ausreichend, um Einstieg Anwendungen, Bibliotheken oder Dienste in Axum erstellen.
Denken Sie daran, dass dies ein Research-Projekt ist, und Benutzer Feedback über die Foren Axum DevLabs-Website anbieten können.
Was geschieht, wenn Axum nicht in einem Produkt Versand- oder Leser verwandeln vorher verwenden möchten?
Zunächst einmal Erfolg oder Misserfolg des Axum hängt wesentlich Teil Benutzer Reaktion, und damit live oder von Qualität Feedback--die wird senden dem Axum-Team Ihre Gedanken und agitate für seine Veröffentlichung in einer öffentlichen!
Aber selbst wenn Axum dir einen Abschluss zu erwerben, denken Sie daran, die Konzepte der Axum sind nicht eindeutig.
In der academic Phraseology Axum verkörpert das Modell Schauspieler, Parallelität und mehrere Akteure-Modelle stehen für den .NET Entwickler für die Produktion heute möchte etwas.
Das Open-Source-Projekt "Retlang"verkörpert einiger dieser Konzepte wie der F#-Sprache (betrachten f# des MailboxProcessor Typ) oder Microsoft Parallelität und der Runtime (CCR), beide sind in der Nähe Lieferung Status nicht bereits vorhanden.
Am Ende Beachten Sie, das Ziel nicht Projekte erstellen, die jede CLR-basierte Sprache vorhanden zu verwenden, sondern nach Sprachen, die bestimmte Probleme zu lösen und wenn die Situation erfordert verwenden können.
Und denken Sie daran, Quot Linguas ruft Tot Homines Vales.
Ted Neward
ist der Principal mit Neward und Partnern, über die er, schreibt und Coaches zum Erstellen von zuverlässigen Unternehmenssystemen spricht.
Er hat zahlreiche Bücher verfasst, unterrichtet und auf Konferenzen überall auf der Welt gesprochen ist ein INETA-Sprecher und hat die MVP-Auszeichnung in drei verschiedenen Bereichen Kenntnisse.
Sie erreichen ihn unter ted@tedneward.com oder über seinen Blog unter blogs.tedneward.com.
|
|
| Tip: Click the printer button in your browser toolbar to get the printer friendly version of this article. |
|