Asynchronous C# and F# (I.): Simultaneous introduction
One of the exciting new technologies that was announced at PDC 2010 is the support for asynchronous programming in C#. So what exactly is asynchronous programming? Many applications today need to connect to some service or external data source including, for example, Web Services and REST APIs. This calls may take a long time to run, but we want to run them without blocking the application.
Currently, you can run the operation on a background thread or using a Task
, but
coordinating multiple such operations is difficult. What if you for example need to wait until
any (or all) of downloads complete and run some more code then? This is not only difficult, but
it also scales badly, because blocking .NET threads is a bad practice (Threads are expensive and when
they're just waiting for other operation to complete, we're wasting valuable resources).
This problem has been the main motivation for including asynchronous workflows in F#
about 3 years ago. In F#, this also enabled various interesting programming styles - for example
creating
GUI using asynchronous workflows (also discussed in Chapter 16 of my book and in
in my recent talk). The C# asynchronous
programming support and the await
keyword is largely inspired by
F# asynchronous workflows (I was quite surprised that F# wasn't more visibly
mentioned in the PDC talk).
In this article series, I'll demonstrate both F# and C# asynchronous programming model, I'll look at features that are missing in one or the other as well as a few subtle differences (that may be unexpected) and finally, I'll write about writing (asynchronous) F# libraries that are easy to use from C#. The plan for the series is following:
- Asynchronous C# and F# (I.): Simultaneous introduction
- Asynchronous C# and F# (II.): How do they differ?
- Asynchronous C# and F# (III.): How does it work?
- Asynchronous C# and F# (IV.): Calling F# libraries from C# (not yet available)
Let's start with a brief overview of asynchronous programming features in C# and F#...
Introducing C# await construct
Perhaps the best example to demonstrate asynchronous programming on .NET is implementing a method
to download the content of a web page. In practice, this can be done easily using WebClient
,
but it works well as a demo. In the implementation, we need to get HTTP response and then
read data from the stream repeatedly in a loop into a buffer.
Writing the code using BeginGetResponse
and BeginRead
methods is
quite difficult. We can pass lambda function as an argument, but there isn't an easy way of
implementing the looping. The problem is that the iteration is started in one scope,
but continues in a different scope (the body of the lambda). As a result, we cannot use
any standard control-flow constructs such as while
, for
but also
try
... catch
.
Downloading page asynchronously
If the feature makes it into C# 5.0 (which I would expect) then we'll be able to write the same
thing more easily using the await
construct. The following snippet shows the initialization of the HTTP request - we'll
discuss the actual downloading in the next snippet.
async Task<Tuple<string, int>> DownloadPage(string url) {
// Create web request to download a web site
var request = HttpWebRequest.Create(url);
// Asynchronously obtain the HTTP response
using (var response = await request.GetResponseAsync()) {
// Start reading the response stream
using (var stream = response.GetResponseStream()) {
// TODO: Asynchronously copy content of the stream
// TODO: Read 'html' content and extract 'title'
return Tuple.Create(title, html.Length);
}
}
}
The method creates a web request and then obtains a HTTP response from the server.
It uses the usual using
statement to ensure that all created
objects will be disposed. At the first look, it looks like a completely ordinary
C# method, but it is not. There are two important differences:
- The
async
keyword andTask
return type - the return type of the method isTask<Tuple<string, int>>
, but there is no task created anywhere in the method. It just creates a tuple and returns it using thereturn
statement. This would look like an error, but the method is also marked withasync
keyword. - The
await
keyword - when getting the response in the secondusing
statement, we use the new keywordawait
and we call an asynchronous variant of the method namedGetResponseAsync
.
The two changes above turn a usual method into an asynchronous method.
When called, the asynchronous method starts running, but returns a task before it completes.
The caller can then do some other work before waiting for the result of the call.
The waiting is done using the new await
construct. The construct can appear
almost anywhere in an expression and we can use it to obtain the result of a Task<T>
or to wait until Task
completes.
Blocking and non-blocking waiting
There is an important difference between writing task.Result
and await task
.
In the first case, the Result
property simply blocks the thread until the
task completes and produces result. On the other hand, the await
construct
is handled in a special way by the C# compiler. The compiler translates the method into a
state machine and replaces all uses of await
with code that sets a continuation
of a task and returns. A continuation is simply a function that will be called when the
task completes and will continue running the body of the method. This requires quite a bit
of reorganization in the body of the method and it is not something that can be easily done by
hand. The key point is that using await
will not block any thread.
Let's now look at the second snippet. It shows a loop that asynchronously downloads the page content using 1kB buffer. When it completes, it extracts title of the page and returns it together with the length of the page:
// Regular expression for extracting page title
Regex regTitle = new Regex(@@"\<title\>([^\<]+)\</title\>");
// Create a buffer and stream to copy data to
var buffer = new byte[1024];
var temp = new MemoryStream();
int count;
do {
// Asynchronously read next 1kB of data
count = await stream.ReadAsync(buffer, 0, buffer.Length);
// Write data to memory stream
temp.Write(buffer, 0, count);
} while (count > 0);
// Read data as string and find page title
temp.Seek(0, SeekOrigin.Begin);
var html = new StreamReader(temp).ReadToEnd();
var title = regTitle.Match(html).Groups[1].Value;
return Tuple.Create(title, html.Length);
Again, this looks like a completely normal C# code with a loop inside.
The interesting thing is that we use await
inside the loop to
implement asynchronous waiting. We start by initializing the buffer and
a memory stream where we copy the downloaded data. Inside the loop, we start
reading from the HTTP stream into a 1kB buffer. This may take some time, so
we use asynchronous version of the operation ReadAsync
, which
returns Task<int>
.
When the reading completes, the system calls a callback that we provided and the callback runs a continuation generated by the C# compiler. The continuation runs the rest of the loop body, checks the condition and then jumps either to the start of the loop (again) or to the code after the loop.
Starting asynchronous operations
Let's now look how we can run the method for a couple of URLs in parallel and
collect all the results. The C# language doesn't provide any direct language
support for doing that, but we can easily do that using a library. The library that is
distributed with the C# preview contains a class TaskEx
(I guess it would be
merged with standard Task
class in a new .NET). The task has a very useful
method named WhenAll
:
async static Task ComparePages() {
var urls = new[] { "http://www.google.com",
"http://www.bing.com", "http://www.yahoo.com", };
var downloads = urls.Select(DownloadPage);
var results = await TaskEx.WhenAll(downloads);
foreach (var item in results)
Console.WriteLine("{0} (length {1})", item.Item1, item.Item2);
}
static void Main() {
ComparePages().Wait();
}
The WhenAll
method (corresponding to Async.Parallel
in F#) takes
a collection of Task<T>
objects and creates a single task that waits until
all the tasks given as an argument complete and returns the results in an array. The return type
of the method is Task<T[]>
. In the code snippet above, we just create a list
of URLs and use the Select
method to call DownloadPage
for every
URL. The result is an array of tasks that are downloading the specified web pages.
Next, we use the await
keyword again to wait until the aggregating task completes
(meaning that all downloads have completed). As a result, we get an array that contains
tuples returned by DownloadPage
, so we can simply iterate over the values in the
array and print the title of the page with the total size in bytes.
The method ComparePages
is again implemented as an asynchronous method
(meaning that it is marked with async
and returns Task
). It doesn't
return any result, but it runs in the background and eventually completes and prints the results.
Since Main
cannot be asynchronous, we cannot call it using await
, but
we can use the standard Wait
method of the Task
type to block the
program until the operation finishes. It is worth noting that when we call ComparePages
,
the operation already starts downloading the web pages, so if your application needs to do
something else, you can just call the method and discard the returned Task
object.
Why do we need asynchronous programming?
You may be wondering why we need asynchronous programming
in the previous example at all. Why can't we just create three threads and run every operation
on a separate thread. If we did that, an operation would occupy the thread for the entire time
needed to download the page. On the other hand, with asynchronous support, a thread is needed only
to run the processing code. An operation called using await
is handled by the system
and it will resume the code (and start using some thread) only after it completes. This means
that when using asynchronous model, the above code could easily run just on a single thread, even
when downloading larger number of pages such as 50.
Using F# asynchronous workflows
Let's now look at the same problem implemented in F#. Asynchronous programming support is
not built directly into the F# language. It is implemented using computation expressions,
which is a general purpose feature for writing computations with some non-standard
aspect. In F#, the same language feature can be used for sequence expressions (similar to
iterators and yield return
) and asynchronous workflows.
Downloading page asynchronously
The .NET methods used and the control flow of the F# version is exactly the same.
The main difference is that I'm using a recursive function instead of while
to implement the looping, but that's just a personal preference. I wanted to show an idiomatic
functional solution (instead of an imperative while
loop), but you can
use the while
loop in F# just fine.
1: let regTitle = new Regex(@@"\<title\>([^\<]+)\</title\>") 2: 3: /// Asynchronously downloads a web page and returns 4: /// title of the page and size in bytes 5: let downloadPage(url:string) = async { 6: let request = HttpWebRequest.Create(url) 7: // Asynchronously get response and dispose it when we're done 8: use! response = request.AsyncGetResponse() 9: use stream = response.GetResponseStream() 10: let temp = new MemoryStream() 11: let buffer = Array.zeroCreate 4096 12: 13: // Loop that downloads page into a buffer (could use 'while' 14: // but recursion is more typical for functional language) 15: let rec download() = async { 16: let! count = stream.AsyncRead(buffer, 0, buffer.Length) 17: do! temp.AsyncWrite(buffer, 0, count) 18: if count > 0 then return! download() } 19: 20: // Start the download asynchronously and handle results 21: do! download() 22: temp.Seek(0L, SeekOrigin.Begin) |> ignore 23: let html = (new StreamReader(temp)).ReadToEnd() 24: return regTitle.Match(html).Groups.[1].Value, html.Length }F# Web Snippets
It is not difficult to see how this corresponds to the C# version we've seen before. The body of the
function is wrapped in an async { ... }
block. Unlike in C#, this is not a hard-coded language
feature - async
is not a keyword but a value defined in F# library that specifies what
is special about the code block (in our case, the fact that it is asynchronous). The block constructs
a value of type Async<string * int>
, which represents an asynchronous computation
that can be started and eventually returns a tuple.
The keywords with bang correspond to various uses of the await
keyword in C#.
We're using use!
keyword to start an asynchronous operation and dispose of the returned object
when the workflow completes, we're using let!
to start an operation and bind the
result to a value and finally, we're using do!
to start operation that doesn't
return any result. I'll discuss some of the differences between F# and C# later
(and in more details in an upcoming article). First, let's look how to start the computation...
Starting asynchronous computations
Just like in the C# version, we'll first write a function comparePages
that downloads
multiple pages in parallel and then prints all the results and returns. We'll write it as an asynchronous
workflow representing the composed computation. Note that unlike in C#, the computation is not
started yet - we're just building a specification of what should be done. Once we have the workflow,
we can start it. In the following example we start it and wait until it completes:
1: /// Downloads pages in parallel and prints all results 2: let comparePages = async { 3: let! results = 4: [| "http://www.google.com"; "http://www.bing.com" 5: "http://www.yahoo.com" |] 6: |> Array.map downloadPage 7: |> Async.Parallel 8: for title, length in results do 9: Console.WriteLine("{0} (length {1})", title, length) } 10: 11: // Start the computation on current thread 12: do comparePages |> Async.RunSynchronouslyF# Web Snippets
The code is again very similar to the C# version earlier in the article. We create an array of
URLs, use Array.map
to turn it into an array of asynchronous computations and then
compose the computations into a single one using Async.Parallel
. This creates a composed
computation that returns an array with all the results. To get the results we wait until the composed
computation completes using let!
Once that happens, we iterate over the results and
output them to the screen.
Unlike in C# (where asynchronous method returns a started task), the F# version just gives us a
specification of the work to be done. We can start it using various different methods. The method
used in the example above is Async.RunSynchronously
, which starts the workflow and blocks
until it completes. However, the sub-tasks done by the workflow (3 distinct downloads) will be all
started in parallel.
Comparing C# and F# asynchronous model
Now that we've seen the same problem implemented in both F# and C#, let's look at some of the similarities and differences between the two programming models. I'll write a separate article with more details and discussing some of the subtle and tricky differences, but I'd like to mention at least a few of them in this introduction.
Similarities
Let's start with the similarities. Obviously, the syntax is quite similar. The await
keyword corresponds to several keywords from F# computation expressions depending on how it is
used. The most common is let!
and then also use!
and do!
These keywords are used to insert a special asynchronous call into code that is otherwise sequential.
There are also some very similar concepts in the libraries, in particular, for introducing parallelism. There are two ways of adding parallelism using asynchronous programming model. One is to compose multiple operations returning the same type (and running the same code) and the other one is to start and wait for tasks explicitly. For the F# way of doing this, see section 4.1 in The F# Asynchronous Programming Model [1] article.
- Data-parallel programming model can be done using the
TaskEx.WhenAll
method in C#. This method corresponds toAsync.Parallel
in F#. These two methods create a task or a workflow that runs multiple operations, waits until all of them complete and then returns all results. - Task-based parallelism in C# is done by creating new task (using
Task.Create
), which is started automatically and then waiting usingawait
. In F#, the same thing is done usingAsync.StartChild
(to start a workflow) andlet!
to wait for the completion.
Differences
Even though the programming model looks almost the same, there are some notable differences. I'll write a more detailed article about some of them, but here is an assorted list of those that I found quite important:
- Availability. This is not a technical aspect, but it is perhaps the most important one. Asynchronous workflows in F# shipped in Visual Studio 2010 and you can use them now to write production code for a wide range of runtimes including .NET 2.0, Mono, Silverlight (see my talk!) and even Windows Phone 7. The bits released for C# are very early version and will (probably) ship with the next version of Visual Studio. My guess is that they will also require a new version of .NET framework when finally released.
- Running or Delayed? The F# programming model creates
Async<T>
which is just a specification of what should be done. We can start it in various different ways (or we may not start it) and it can be quite nicely composed. The C# programming model works withTask<T>
type, which means thatasync
method creates a computation that is already running in background. The F# programming model definitely feels more suitable for functional (declarative) programming languages. I also think that it makes it easier to reason about what is going on. - Cancellation. When you want to implement a computation that can be cancelled in C#, you
need to explicitly pass
CancellationToken
object around and check whether a cancellation was requested. In F#, this is done automatically (and checks are made each time some asynchronous operation is executed (or when a language construct with special meaning is executed). Implementing the cancellation explicitly isn't as bad as writing everything usingBeginFoo
, but doing it properly is not as easy as it may sound (more about that later) - Waiting inside expressions. In C#, we can use
await
anywhere inside an expression, while in F#,let!
cannot be nested. When you want to rewrite something likeawait Foo(await Bar())
in F#, you first have to assign the result ofBar
to some local variable. This is a good thing for C#, because it makes writing of certain things easier. A slight disadvantage may be that it is not as easy to see when an asynchronous operation occurs in the expression (though it would be interesting to see this feature in F# too).
Summary
As far as I can tell, the asynchronous programming model in C# is largely based on F# asynchronous workflows. It is wonderful to see how ideas from the F# language spread to the main-stream (although it would be nicer if this was more visibly acknowledged in the presentation).
This definitely doesn't mean that F# is not interesting anymore. There are many useful features in F# not related to asynchronous programming that would be difficult to imagine in C#. Even when talking just about asynchronous programming, F# still has some features and libraries that make it more attractive. Two examples are implicit cancellation and a wonderful library for agent-based parallelism. Moreover, there are interesting new things in F# comming out at PDC 2010. There are also interesting research projects (like my pattern matching extension [3]) which would make F# even better for reactive, concurrent and asynchronous programming (but, just to be clear, that's a distant future).
References
- [1] The F# Asynchronous Programming Model - Don Syme, Tomas Petricek, Dmitry Lomov
- [2] Asynchronous Programming for C# and Visual Basic - MSDN homepage
- [3] Joinads: A retargetable control-flow construct for reactive, parallel and concurrent programming - Tomas Petricek, Don Syme
Full name: Demo.regTitle
type: Regex
implements: Runtime.Serialization.ISerializable
class
new : string -> System.Text.RegularExpressions.Regex
new : string * System.Text.RegularExpressions.RegexOptions -> System.Text.RegularExpressions.Regex
member GetGroupNames : unit -> string []
member GetGroupNumbers : unit -> int []
member GroupNameFromNumber : int -> string
member GroupNumberFromName : string -> int
member IsMatch : string -> bool
member IsMatch : string * int -> bool
member Match : string -> System.Text.RegularExpressions.Match
member Match : string * int -> System.Text.RegularExpressions.Match
member Match : string * int * int -> System.Text.RegularExpressions.Match
member Matches : string -> System.Text.RegularExpressions.MatchCollection
member Matches : string * int -> System.Text.RegularExpressions.MatchCollection
member Options : System.Text.RegularExpressions.RegexOptions
member Replace : string * string -> string
member Replace : string * System.Text.RegularExpressions.MatchEvaluator -> string
member Replace : string * string * int -> string
member Replace : string * System.Text.RegularExpressions.MatchEvaluator * int -> string
member Replace : string * string * int * int -> string
member Replace : string * System.Text.RegularExpressions.MatchEvaluator * int * int -> string
member RightToLeft : bool
member Split : string -> string []
member Split : string * int -> string []
member Split : string * int * int -> string []
member ToString : unit -> string
static member CacheSize : int with get, set
static member CompileToAssembly : System.Text.RegularExpressions.RegexCompilationInfo [] * System.Reflection.AssemblyName -> unit
static member CompileToAssembly : System.Text.RegularExpressions.RegexCompilationInfo [] * System.Reflection.AssemblyName * System.Reflection.Emit.CustomAttributeBuilder [] -> unit
static member CompileToAssembly : System.Text.RegularExpressions.RegexCompilationInfo [] * System.Reflection.AssemblyName * System.Reflection.Emit.CustomAttributeBuilder [] * string -> unit
static member Escape : string -> string
static member IsMatch : string * string -> bool
static member IsMatch : string * string * System.Text.RegularExpressions.RegexOptions -> bool
static member Match : string * string -> System.Text.RegularExpressions.Match
static member Match : string * string * System.Text.RegularExpressions.RegexOptions -> System.Text.RegularExpressions.Match
static member Matches : string * string -> System.Text.RegularExpressions.MatchCollection
static member Matches : string * string * System.Text.RegularExpressions.RegexOptions -> System.Text.RegularExpressions.MatchCollection
static member Replace : string * string * string -> string
static member Replace : string * string * System.Text.RegularExpressions.MatchEvaluator -> string
static member Replace : string * string * string * System.Text.RegularExpressions.RegexOptions -> string
static member Replace : string * string * System.Text.RegularExpressions.MatchEvaluator * System.Text.RegularExpressions.RegexOptions -> string
static member Split : string * string -> string []
static member Split : string * string * System.Text.RegularExpressions.RegexOptions -> string []
static member Unescape : string -> string
end
Full name: System.Text.RegularExpressions.Regex
type: Regex
implements: Runtime.Serialization.ISerializable
Full name: Demo.downloadPage
Asynchronously downloads a web page and returns
title of the page and size in bytes
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
val string : 'T -> string
Full name: Microsoft.FSharp.Core.Operators.string
--------------------
type string = String
Full name: Microsoft.FSharp.Core.string
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
type: WebRequest
implements: Runtime.Serialization.ISerializable
inherits: MarshalByRefObject
class
inherit System.Net.WebRequest
member Abort : unit -> unit
member Accept : string with get, set
member AddRange : int -> unit
member AddRange : int * int -> unit
member AddRange : string * int -> unit
member AddRange : string * int * int -> unit
member Address : System.Uri
member AllowAutoRedirect : bool with get, set
member AllowWriteStreamBuffering : bool with get, set
member AutomaticDecompression : System.Net.DecompressionMethods with get, set
member BeginGetRequestStream : System.AsyncCallback * obj -> System.IAsyncResult
member BeginGetResponse : System.AsyncCallback * obj -> System.IAsyncResult
member ClientCertificates : System.Security.Cryptography.X509Certificates.X509CertificateCollection with get, set
member Connection : string with get, set
member ConnectionGroupName : string with get, set
member ContentLength : int64 with get, set
member ContentType : string with get, set
member ContinueDelegate : System.Net.HttpContinueDelegate with get, set
member CookieContainer : System.Net.CookieContainer with get, set
member Credentials : System.Net.ICredentials with get, set
member EndGetRequestStream : System.IAsyncResult -> System.IO.Stream
member EndGetRequestStream : System.IAsyncResult * System.Net.TransportContext -> System.IO.Stream
member EndGetResponse : System.IAsyncResult -> System.Net.WebResponse
member Expect : string with get, set
member GetRequestStream : unit -> System.IO.Stream
member GetRequestStream : System.Net.TransportContext -> System.IO.Stream
member GetResponse : unit -> System.Net.WebResponse
member HaveResponse : bool
member Headers : System.Net.WebHeaderCollection with get, set
member IfModifiedSince : System.DateTime with get, set
member KeepAlive : bool with get, set
member MaximumAutomaticRedirections : int with get, set
member MaximumResponseHeadersLength : int with get, set
member MediaType : string with get, set
member Method : string with get, set
member Pipelined : bool with get, set
member PreAuthenticate : bool with get, set
member ProtocolVersion : System.Version with get, set
member Proxy : System.Net.IWebProxy with get, set
member ReadWriteTimeout : int with get, set
member Referer : string with get, set
member RequestUri : System.Uri
member SendChunked : bool with get, set
member ServicePoint : System.Net.ServicePoint
member Timeout : int with get, set
member TransferEncoding : string with get, set
member UnsafeAuthenticatedConnectionSharing : bool with get, set
member UseDefaultCredentials : bool with get, set
member UserAgent : string with get, set
static member DefaultCachePolicy : System.Net.Cache.RequestCachePolicy with get, set
static member DefaultMaximumErrorResponseLength : int with get, set
static member DefaultMaximumResponseHeadersLength : int with get, set
end
Full name: System.Net.HttpWebRequest
type: HttpWebRequest
implements: Runtime.Serialization.ISerializable
inherits: WebRequest
inherits: MarshalByRefObject
WebRequest.Create(requestUri: Uri) : WebRequest
WebRequest.Create(requestUriString: string) : WebRequest
type: WebResponse
implements: Runtime.Serialization.ISerializable
implements: IDisposable
inherits: MarshalByRefObject
type: Stream
implements: IDisposable
inherits: MarshalByRefObject
type: MemoryStream
implements: IDisposable
inherits: Stream
inherits: MarshalByRefObject
class
inherit System.IO.Stream
new : unit -> System.IO.MemoryStream
new : int -> System.IO.MemoryStream
new : System.Byte [] -> System.IO.MemoryStream
new : System.Byte [] * bool -> System.IO.MemoryStream
new : System.Byte [] * int * int -> System.IO.MemoryStream
new : System.Byte [] * int * int * bool -> System.IO.MemoryStream
new : System.Byte [] * int * int * bool * bool -> System.IO.MemoryStream
member CanRead : bool
member CanSeek : bool
member CanWrite : bool
member Capacity : int with get, set
member Flush : unit -> unit
member GetBuffer : unit -> System.Byte []
member Length : int64
member Position : int64 with get, set
member Read : System.Byte [] * int * int -> int
member ReadByte : unit -> int
member Seek : int64 * System.IO.SeekOrigin -> int64
member SetLength : int64 -> unit
member ToArray : unit -> System.Byte []
member Write : System.Byte [] * int * int -> unit
member WriteByte : System.Byte -> unit
member WriteTo : System.IO.Stream -> unit
end
Full name: System.IO.MemoryStream
type: MemoryStream
implements: IDisposable
inherits: Stream
inherits: MarshalByRefObject
type: byte []
implements: ICloneable
implements: Collections.IList
implements: Collections.ICollection
implements: Collections.Generic.IList<byte>
implements: Collections.Generic.ICollection<byte>
implements: seq<byte>
implements: Collections.IEnumerable
inherits: Array
class
member Clone : unit -> obj
member CopyTo : System.Array * int -> unit
member CopyTo : System.Array * int64 -> unit
member GetEnumerator : unit -> System.Collections.IEnumerator
member GetLength : int -> int
member GetLongLength : int -> int64
member GetLowerBound : int -> int
member GetUpperBound : int -> int
member GetValue : int [] -> obj
member GetValue : int -> obj
member GetValue : int64 -> obj
member GetValue : int64 [] -> obj
member GetValue : int * int -> obj
member GetValue : int64 * int64 -> obj
member GetValue : int * int * int -> obj
member GetValue : int64 * int64 * int64 -> obj
member Initialize : unit -> unit
member IsFixedSize : bool
member IsReadOnly : bool
member IsSynchronized : bool
member Length : int
member LongLength : int64
member Rank : int
member SetValue : obj * int -> unit
member SetValue : obj * int [] -> unit
member SetValue : obj * int64 -> unit
member SetValue : obj * int64 [] -> unit
member SetValue : obj * int * int -> unit
member SetValue : obj * int64 * int64 -> unit
member SetValue : obj * int * int * int -> unit
member SetValue : obj * int64 * int64 * int64 -> unit
member SyncRoot : obj
static member AsReadOnly<'T> : 'T [] -> System.Collections.ObjectModel.ReadOnlyCollection<'T>
static member BinarySearch : System.Array * obj -> int
static member BinarySearch<'T> : 'T [] * 'T -> int
static member BinarySearch : System.Array * obj * System.Collections.IComparer -> int
static member BinarySearch<'T> : 'T [] * 'T * System.Collections.Generic.IComparer<'T> -> int
static member BinarySearch : System.Array * int * int * obj -> int
static member BinarySearch<'T> : 'T [] * int * int * 'T -> int
static member BinarySearch : System.Array * int * int * obj * System.Collections.IComparer -> int
static member BinarySearch<'T> : 'T [] * int * int * 'T * System.Collections.Generic.IComparer<'T> -> int
static member Clear : System.Array * int * int -> unit
static member ConstrainedCopy : System.Array * int * System.Array * int * int -> unit
static member ConvertAll<'TInput,'TOutput> : 'TInput [] * System.Converter<'TInput,'TOutput> -> 'TOutput []
static member Copy : System.Array * System.Array * int -> unit
static member Copy : System.Array * System.Array * int64 -> unit
static member Copy : System.Array * int * System.Array * int * int -> unit
static member Copy : System.Array * int64 * System.Array * int64 * int64 -> unit
static member CreateInstance : System.Type * int -> System.Array
static member CreateInstance : System.Type * int [] -> System.Array
static member CreateInstance : System.Type * int64 [] -> System.Array
static member CreateInstance : System.Type * int * int -> System.Array
static member CreateInstance : System.Type * int [] * int [] -> System.Array
static member CreateInstance : System.Type * int * int * int -> System.Array
static member Exists<'T> : 'T [] * System.Predicate<'T> -> bool
static member Find<'T> : 'T [] * System.Predicate<'T> -> 'T
static member FindAll<'T> : 'T [] * System.Predicate<'T> -> 'T []
static member FindIndex<'T> : 'T [] * System.Predicate<'T> -> int
static member FindIndex<'T> : 'T [] * int * System.Predicate<'T> -> int
static member FindIndex<'T> : 'T [] * int * int * System.Predicate<'T> -> int
static member FindLast<'T> : 'T [] * System.Predicate<'T> -> 'T
static member FindLastIndex<'T> : 'T [] * System.Predicate<'T> -> int
static member FindLastIndex<'T> : 'T [] * int * System.Predicate<'T> -> int
static member FindLastIndex<'T> : 'T [] * int * int * System.Predicate<'T> -> int
static member ForEach<'T> : 'T [] * System.Action<'T> -> unit
static member IndexOf : System.Array * obj -> int
static member IndexOf<'T> : 'T [] * 'T -> int
static member IndexOf : System.Array * obj * int -> int
static member IndexOf<'T> : 'T [] * 'T * int -> int
static member IndexOf : System.Array * obj * int * int -> int
static member IndexOf<'T> : 'T [] * 'T * int * int -> int
static member LastIndexOf : System.Array * obj -> int
static member LastIndexOf<'T> : 'T [] * 'T -> int
static member LastIndexOf : System.Array * obj * int -> int
static member LastIndexOf<'T> : 'T [] * 'T * int -> int
static member LastIndexOf : System.Array * obj * int * int -> int
static member LastIndexOf<'T> : 'T [] * 'T * int * int -> int
static member Resize<'T> : 'T [] * int -> unit
static member Reverse : System.Array -> unit
static member Reverse : System.Array * int * int -> unit
static member Sort : System.Array -> unit
static member Sort<'T> : 'T [] -> unit
static member Sort : System.Array * System.Array -> unit
static member Sort : System.Array * System.Collections.IComparer -> unit
static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] -> unit
static member Sort<'T> : 'T [] * System.Collections.Generic.IComparer<'T> -> unit
static member Sort<'T> : 'T [] * System.Comparison<'T> -> unit
static member Sort : System.Array * int * int -> unit
static member Sort : System.Array * System.Array * System.Collections.IComparer -> unit
static member Sort<'T> : 'T [] * int * int -> unit
static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] * System.Collections.Generic.IComparer<'TKey> -> unit
static member Sort : System.Array * System.Array * int * int -> unit
static member Sort : System.Array * int * int * System.Collections.IComparer -> unit
static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] * int * int -> unit
static member Sort<'T> : 'T [] * int * int * System.Collections.Generic.IComparer<'T> -> unit
static member Sort : System.Array * System.Array * int * int * System.Collections.IComparer -> unit
static member Sort<'TKey,'TValue> : 'TKey [] * 'TValue [] * int * int * System.Collections.Generic.IComparer<'TKey> -> unit
static member TrueForAll<'T> : 'T [] * System.Predicate<'T> -> bool
end
Full name: System.Array
type: Array
implements: ICloneable
implements: Collections.IList
implements: Collections.ICollection
implements: Collections.IEnumerable
Full name: Microsoft.FSharp.Collections.Array.zeroCreate
type: int
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType
member Stream.AsyncRead : count:int -> Async<byte []>
member Stream.AsyncRead : buffer:byte [] * ?offset:int * ?count:int -> Async<int>
| Begin = 0
| Current = 1
| End = 2
Full name: System.IO.SeekOrigin
type: SeekOrigin
inherits: Enum
inherits: ValueType
Full name: Microsoft.FSharp.Core.Operators.ignore
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
class
inherit System.IO.TextReader
new : System.IO.Stream -> System.IO.StreamReader
new : System.IO.Stream * bool -> System.IO.StreamReader
new : System.IO.Stream * System.Text.Encoding -> System.IO.StreamReader
new : System.IO.Stream * System.Text.Encoding * bool -> System.IO.StreamReader
new : System.IO.Stream * System.Text.Encoding * bool * int -> System.IO.StreamReader
new : string -> System.IO.StreamReader
new : string * bool -> System.IO.StreamReader
new : string * System.Text.Encoding -> System.IO.StreamReader
new : string * System.Text.Encoding * bool -> System.IO.StreamReader
new : string * System.Text.Encoding * bool * int -> System.IO.StreamReader
member BaseStream : System.IO.Stream
member Close : unit -> unit
member CurrentEncoding : System.Text.Encoding
member DiscardBufferedData : unit -> unit
member EndOfStream : bool
member Peek : unit -> int
member Read : unit -> int
member Read : char [] * int * int -> int
member ReadLine : unit -> string
member ReadToEnd : unit -> string
static val Null : System.IO.StreamReader
end
Full name: System.IO.StreamReader
type: StreamReader
implements: IDisposable
inherits: TextReader
inherits: MarshalByRefObject
Regex.Match(input: string) : Match
Regex.Match(input: string, startat: int) : Match
Regex.Match(input: string, beginning: int, length: int) : Match
Full name: Demo.comparePages
Downloads pages in parallel and prints all results
type: (string * int) []
implements: ICloneable
implements: Collections.IList
implements: Collections.ICollection
implements: Collections.Generic.IList<string * int>
implements: Collections.Generic.ICollection<string * int>
implements: seq<string * int>
implements: Collections.IEnumerable
inherits: Array
Full name: Microsoft.FSharp.Collections.Array.map
type Async<'T>
Full name: Microsoft.FSharp.Control.Async<_>
--------------------
type Async
with
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg:'Arg1 * beginAction:('Arg1 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * beginAction:('Arg1 * 'Arg2 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromBeginEnd : arg1:'Arg1 * arg2:'Arg2 * arg3:'Arg3 * beginAction:('Arg1 * 'Arg2 * 'Arg3 * AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T>
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
static member Ignore : computation:Async<'T> -> Async<unit>
static member OnCancel : interruption:(unit -> unit) -> Async<IDisposable>
static member Parallel : computations:seq<Async<'T>> -> Async<'T []>
static member RunSynchronously : computation:Async<'T> * ?timeout:int * ?cancellationToken:CancellationToken -> 'T
static member Sleep : millisecondsDueTime:int -> Async<unit>
static member Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartChild : computation:Async<'T> * ?millisecondsTimeout:int -> Async<Async<'T>>
static member StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
static member StartWithContinuations : computation:Async<'T> * continuation:('T -> unit) * exceptionContinuation:(exn -> unit) * cancellationContinuation:(OperationCanceledException -> unit) * ?cancellationToken:CancellationToken -> unit
static member SwitchToContext : syncContext:SynchronizationContext -> Async<unit>
static member SwitchToNewThread : unit -> Async<unit>
static member SwitchToThreadPool : unit -> Async<unit>
static member TryCancelled : computation:Async<'T> * compensation:(OperationCanceledException -> unit) -> Async<'T>
static member CancellationToken : Async<CancellationToken>
static member DefaultCancellationToken : CancellationToken
end
Full name: Microsoft.FSharp.Control.Async
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
type: int
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType
class
static member BackgroundColor : System.ConsoleColor with get, set
static member Beep : unit -> unit
static member Beep : int * int -> unit
static member BufferHeight : int with get, set
static member BufferWidth : int with get, set
static member CapsLock : bool
static member Clear : unit -> unit
static member CursorLeft : int with get, set
static member CursorSize : int with get, set
static member CursorTop : int with get, set
static member CursorVisible : bool with get, set
static member Error : System.IO.TextWriter
static member ForegroundColor : System.ConsoleColor with get, set
static member In : System.IO.TextReader
static member InputEncoding : System.Text.Encoding with get, set
static member KeyAvailable : bool
static member LargestWindowHeight : int
static member LargestWindowWidth : int
static member MoveBufferArea : int * int * int * int * int * int -> unit
static member MoveBufferArea : int * int * int * int * int * int * char * System.ConsoleColor * System.ConsoleColor -> unit
static member NumberLock : bool
static member OpenStandardError : unit -> System.IO.Stream
static member OpenStandardError : int -> System.IO.Stream
static member OpenStandardInput : unit -> System.IO.Stream
static member OpenStandardInput : int -> System.IO.Stream
static member OpenStandardOutput : unit -> System.IO.Stream
static member OpenStandardOutput : int -> System.IO.Stream
static member Out : System.IO.TextWriter
static member OutputEncoding : System.Text.Encoding with get, set
static member Read : unit -> int
static member ReadKey : unit -> System.ConsoleKeyInfo
static member ReadKey : bool -> System.ConsoleKeyInfo
static member ReadLine : unit -> string
static member ResetColor : unit -> unit
static member SetBufferSize : int * int -> unit
static member SetCursorPosition : int * int -> unit
static member SetError : System.IO.TextWriter -> unit
static member SetIn : System.IO.TextReader -> unit
static member SetOut : System.IO.TextWriter -> unit
static member SetWindowPosition : int * int -> unit
static member SetWindowSize : int * int -> unit
static member Title : string with get, set
static member TreatControlCAsInput : bool with get, set
static member WindowHeight : int with get, set
static member WindowLeft : int with get, set
static member WindowTop : int with get, set
static member WindowWidth : int with get, set
static member Write : bool -> unit
static member Write : char -> unit
static member Write : char [] -> unit
static member Write : float -> unit
static member Write : decimal -> unit
static member Write : float32 -> unit
static member Write : int -> unit
static member Write : uint32 -> unit
static member Write : int64 -> unit
static member Write : uint64 -> unit
static member Write : obj -> unit
static member Write : string -> unit
static member Write : string * obj -> unit
static member Write : string * obj [] -> unit
static member Write : string * obj * obj -> unit
static member Write : char [] * int * int -> unit
static member Write : string * obj * obj * obj -> unit
static member Write : string * obj * obj * obj * obj -> unit
static member WriteLine : unit -> unit
static member WriteLine : bool -> unit
static member WriteLine : char -> unit
static member WriteLine : char [] -> unit
static member WriteLine : decimal -> unit
static member WriteLine : float -> unit
static member WriteLine : float32 -> unit
static member WriteLine : int -> unit
static member WriteLine : uint32 -> unit
static member WriteLine : int64 -> unit
static member WriteLine : uint64 -> unit
static member WriteLine : obj -> unit
static member WriteLine : string -> unit
static member WriteLine : string * obj -> unit
static member WriteLine : string * obj [] -> unit
static member WriteLine : char [] * int * int -> unit
static member WriteLine : string * obj * obj -> unit
static member WriteLine : string * obj * obj * obj -> unit
static member WriteLine : string * obj * obj * obj * obj -> unit
end
Full name: System.Console
Console.WriteLine() : unit
Console.WriteLine(value: string) : unit
Console.WriteLine(value: obj) : unit
Console.WriteLine(value: uint64) : unit
Console.WriteLine(value: int64) : unit
Console.WriteLine(value: uint32) : unit
Console.WriteLine(value: int) : unit
Console.WriteLine(value: float32) : unit
Console.WriteLine(value: float) : unit
Console.WriteLine(value: decimal) : unit
(+9 other overloads)
Published: Friday, 29 October 2010, 4:34 AM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: c#, asynchronous, f#