Asynchronous C# and F# (II.): How do they differ?
Anders Hejlsberg announced the support for asynchronous programming in the next version of C# announced at PDC 2010. The extension looks very similar to F# asynchronous workflows (which are already available in Visual Studio 2010). Despite the large similarity, there are a couple of differences between the two programming models. I already mentioned some of them briefly in the first article of this series. In this article, we'll look at some of the technical differences between the two models.
The most notable non-technical difference is that F# asynchronous workflows are already available in a supported version of Visual Studio and can be used on a large number of .NET runtimes (including, for example, Windows Phone and Silverlight). However, there are surprisingly many technical differences as well. In this article, I'll talk about the support for cancellation, which is available in F# and is not (currently) available in the C# preview. We'll also look at the difference between model where tasks are created as running and the delayed model.
This article is the second part of a short series about the asynchronous programming support in C# and F#. The previous article shows a simple example in both of the languages and the next articles explain how does the feature work (and how you can customize its behavior) as well as how to call F# libraries from C#:
- 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 by looking at, perhaps, the most important technical difference - the built-in support for cancellation available in F# asynchronous workflows.
Cancellation support
In .NET, cancellation is typically implemented using the CancellationToken
object.
The object needs to be passed around through the whole computation and the computation needs to check if
a cancellation was requested. Callers of the computation will create CancellationTokenSource
and then give it to the computation. When they decide that the computation should be cancelled, they
request cancellation by calling the Cancel
method.
In F# asynchronous workflows, the CancellationToken
object is passed around automatically
under the cover. This means that we don't have to do anything special to support cancellation. When
running asynchronous workflow, we can give it cancellation token and everything will work automatically.
On the other hand, C# doesn't provide any automatic support for doing this. The recommended pattern is
to add additional CancellationToken
as an additional parameter to all asynchronous methods
and check for cancellation regularly.
Using cancellation support in F#
We'll start by looking at an example in F# and then we'll try to implement the same behavior using C#. I'll use the same example as in the previous article - downloading the content of a web page. The following code snippet is exactly the same F# asynchronous workflow as in the previous article. I'm repeating it here so that you don't have to switch between pages, but we didn't have to do any change:
1: /// Asynchronously downloads a web page and returns 2: /// title of the page and size in bytes 3: let downloadPage(url:string) = async { 4: (Initialization of web request omitted) 5: 6: // Loop that downloads page into a buffer (could use 'while' 7: // but recursion is more typical for functional language) 8: let rec download() = async { 9: let! count = stream.AsyncRead(buffer, 0, buffer.Length) 10: do! temp.AsyncWrite(buffer, 0, count) 11: if count > 0 then return! download() } 12: 13: // Start the download asynchronously and handle results 14: do! download() 15: (Construction of the result omitted) 16: return regTitle.Match(html).Groups.[1].Value, html.Length } 17: 18: /// Downloads pages in parallel and prints all results 19: let comparePages = async { 20: let! results = 21: [| "http://www.google.com"; "http://www.bing.com" 22: "http://www.yahoo.com" |] 23: |> Array.map downloadPage 24: |> Async.Parallel 25: for title, length in results do 26: Console.WriteLine("{0} (length {1})", title, length) }F# Web Snippets
The snippet was already explained in the previous article, but there are a few interesting points from
the cancellation perspective. The workflow performs the first asynchronous operation at the beginning
when calling AsyncGetResponse
(place cursor over the hidden bit to see it). Then it starts
a loop which performs another asynchronous operation - AsyncRead
- repeatedly until to
download is complete. There is a bit of processing between these asynchronous operations, but this
won't usually take a long time (but if we included some CPU-intensive computations, there may be code block
that would take quite a long time).
As already mentioned, we didn't need to do anything so far to support cancellation in asynchronous workflows.
The only change is that we need to create and pass CancellationToken
to the workflow when starting
it. The following snippet starts the above computation and leaves it running in the background. If the user
hits Enter before it has completed, it will be cancelled using the Cancel
method:
1: // Start workflow with cancellation token set 2: let cts = new CancellationTokenSource() 3: Async.Start(comparePages, cts.Token) 4: // Cancel download when user hits Enter 5: Console.ReadLine() |> ignore 6: cts.Cancel() 7: Console.WriteLine("Finished")F# Web Snippets
We're starting the workflow using Async.Start
which schedules the work on a background thread
and then returns control back to the caller. Then we call Console.ReadLine
to wait for a user input.
If the workflow is still running in the background after the user hits Enter, it will be cancelled and
we print a confirmation. In a typical GUI scenario, you would store the cancellation token somewhere in your
application and implement a "Cancel" button that calls the Cancel
method. If you want to see more examples,
you can look at some of my earlier blog posts. I used this pattern for example in
Financial dashboard example or
in the implementation of F# Web Snippets.
Let's now look how we can implement the same behavior in C# by passing cancellation token explicitly...
Implementing cancellation in C#
As already mentioned, in C# we need to pass the CancellationToken
explicitly to all
asynchronous methods that support cancellation. I'll follow the pattern recommended in the documentation
and add Async
as the postfix to the method name and I'll add a last parameter named
cancellationToken
of the CancellationToken
type. This naming is also
recommended in the design documents, but it feels really verbose to me.
Once we have the cancellation token, we need to repeatedly check if the cancellation was requested. It is generally a good idea to check before and after each asynchronous operation (as these may take most of the time). Most importantly, we need to check inside the loop, because this is probably a place where the method spends the most of the time:
async Task<Tuple<string, int>> DownloadPageAsync
(string url, CancellationToken cancellationToken) {
var request = HttpWebRequest.Create(url);
// Check if cancellation was triggered
cancellationToken.ThrowIfCancellationRequested();
using (var response = await request.GetResponseAsync())
using (var stream = response.GetResponseStream()) {
// Check if cancellation was triggered
cancellationToken.ThrowIfCancellationRequested();
var buffer = new byte[4096];
var temp = new MemoryStream();
int count;
do {
// Check if cancellation was triggered (at each loop iteration)
cancellationToken.ThrowIfCancellationRequested();
count = await stream.ReadAsync(buffer, 0, buffer.Length);
await temp.WriteAsync(buffer, 0, count);
} while (count > 0);
temp.Seek(0, SeekOrigin.Begin);
var html = new StreamReader(temp).ReadToEnd();
return Tuple.Create(regTitle.Match(html).Groups[1].Value, html.Length);
}
}
There are a few ways to implement the check for cancellation. You can check if the
IsCancellationRequested
property is true
and stop the current
operation. Another option is to call ThrowIfCancellationRequested
which
throws OperationCancelled
exception if the task should be cancelled. I'm
using the second approach in the above example, because it is easier and also because
we wouldn't have any result to return if the operation is cancelled before it completes.
The CancellationToken
parameter needs to be added to all methods in the
call hierarchy, which means that we need to add it to the second method from the example
as well. The method calls DownloadPageAsync
multiple times in parallel and
we need to pass the CancellationToken
to every instance of the method (so that
all of them are cancelled when the user requests it). We could also check for cancellation
inside the body of the method, but I didn't add that, because when the await
call completes, we only need to print the results:
async Task ComparePagesAsync(CancellationToken cancellationToken) {
try {
var urls = new[] { "http://www.google.com",
"http://www.bing.com", "http://www.yahoo.com", };
// Pass cancellation token to the method we're calling explicitly
var downloads = urls.Select(url =>
DownloadPageAsync(url, cancellationToken));
var results = await TaskEx.WhenAll(downloads);
foreach (var item in results)
Console.WriteLine("{0} (length {1})", item.Item1, item.Item2);
} catch(OperationCancelledException) {
Console.WriteLine("Finished");
}
}
As the last thing, we'll take a look at the Main
method. We'll create a
cancellation token and then start the download by calling ComparePagesAsync
passing it the cancellation token as an argument:
static void Main() {
// Create cancellation token and give it to the asynchronous method
var cts = new CancellationTokenSource();
ComparePagesAsync(cts.Token);
// Cancel download when the user hits Enter
Console.ReadLine();
cts.Cancel();
Console.ReadLine();
}
The only difference between the above C# code and an earlier F# version is that
in C#, we don't have to do anything special to start the operation. In F#, we started
it explicitly by calling Async.Start
, but in C#, the returned Task
is
already started. This is one a few differences in the control flow of the code written using
C# asynchronous methods and F# asynchronous workflows. Let's look at them in a more detail...
Control flow differences
In the C# programming model, asynchronous methods return objects of type Task<T>
or Task
when returning a void
. On the other hand, F# uses its own
type named Async<T>
(the .NET name of the type is FSharpAsync<T>
).
This allows F# to work even on platforms that do not support .NET 4.0 Task Parallel Library, but the
two types are also quite different...
Understanding tasks and task generators
In the paper about asynchronous programming model in F# [1] (written by Don Syme, myself and Dmitry Lomov), we mentioned three possible options that can be used for implementing the asynchronous programming model:
- Hot tasks (used by C#) - in this case, the asynchronous code block returns a task that has been already started and will eventually produce a value.
- Cold tasks - in this model, the code block also returns a task that will eventually produce a value, but it doesn't start the task. The caller is responsible for starting it and may decide not to start it at all.
- Task generators (used by F#) - in this case, the code block returns an object that will generate and start a task when it is provided with a continuation to be called when the operation completes.
The choice of the programming model has some interesting implications. For example, in the hot tasks model, we cannot run the asynchronous method and then decide that we don't want to execute it, because it has been already started. The following example demonstrates what I mean.
Let's start by looking at the F# version of the code. The central point of the example
is the function printTitle
which takes two asynchronous workflows as arguments
and runs one of them depending on some configuration. When the main server is running, we use
the first parameter and when it is down, we use the second parameter:
1: /// Download page content and return the title 2: let downloadPage url = async { 3: printfn "Downloading %s" url 4: let wc = new WebClient() 5: let! html = wc.AsyncDownloadString(Uri(url)) 6: return regTitle.Match(html).Groups.[1].Value } 7: 8: /// Prints title of a page. Uses a backup server if the 9: /// main server is not available at the moment 10: let printTitle main backup = async { 11: let! title = if useMainServer then main else backup 12: printfn "%s" title } 13: 14: // Start the work with main and backup workflow as arguments 15: printTitle 16: (downloadPage "http://fsharp.net") 17: (downloadPage "http://backup.fsharp.net") 18: |> Async.StartF# Web Snippets
Now that we discussed the difference between hot tasks and task generators
you probably already see where the example goes. If you run the above F# version, it will call
the downloadUrl
function twice (to evaluate the arguments when calling printTitle
).
However, the function only returns a task generator, which means that the printfn
function on the first line will not be called. The task generators are then passed to the
printTitle
function, which chooses one of them. Note that printTitle
also
doesn't run the task generator - it just returns another one which, when provided with the
continuation, will eventually call the original task generator.
If we just literally translated the code to C# then the behavior would be quite different:
// Download page content and return the title
static async Task<string> DownloadPageAsync(string url) {
Console.WriteLine("Downloading {0}", url);
var wc = new WebClient();
var html = await wc.DownloadStringTaskAsync(new Uri(url));
return regTitle.Match(html).Groups[1].Value;
}
// Prints title of a page. Uses a backup server if the
// main server is not available at the moment
static async Task PrintTitleAsync(Task<string> main, Task<string> backup) {
var title = await (useMainServer?main:backup);
Console.WriteLine(title);
}
// Start the work with main and backup workflow as arguments
static void Main() {
PrintTitleAsync
( DownloadPageAsync("http://fsharp.net"),
DownloadPageAsync("http://backup.fsharp.net") ).Wait();
}
When we run the Main
method, it evaluates the parameters of the PrintTitleAsync
method. This returns the task, but as a side-effect it also starts the task, so the application
will start downloading both of the pages even though we'll need just one of them later. Note that
if the download that is not used throws an exception, the exception will not be propagated further
(because we don't access the result of the task). As a result, the code will give the same result as
the F# version (aside from the additional printed line), but it will do some additional
unnecessary work.
Of course, this is just a design decision and there is nothing wrong with the C# style. Using task generators makes more sense in a functional language such as F#, because it avoids side-effects and changing state. For example, the fact whether a task is running is a mutable state, which may cause some problems and the starting of a task when it is created is an example of side-effect. The model used in F# is more declarative in a sense that we specify what should be done as a workflow and then evaluate the workflow at once (which can be even a synchronous operation).
Another interesting aspect is the implementation. From my experience, the hot tasks model is the most difficult to implement - the problem is that when a code block returns a task that has been already started, it may also already completed and generated the result. This means that we need to handle a case when the task has already completed (e.g. by testing some flag and continuing if it is set) and also a case when the task is still running (e.g. by setting a continuation to be called later). On the other hand, the task generators model is quite easy to implement, because we just need to pass the continuation to other generators. The implementation makes often use of tail-calls, so it is more reasonable to use it in a functional language. It can also quite nicely work with recursive definitions of computations, which is again needed mostly in functional languages.
How is background work started?
Let's look at the next difference between the two programming models. The key point of asynchronous programming is to start an operation without blocking a thread - typically by running it on a background thread or by sending some instruction to the system and providing a callback. When we write some asynchronous computation, we want to know exactly which parts of the computation are going to be called on the current thread and which parts are going to run in the background (and on what thread).
The interesting moment is when starting an asynchronous computation. When we use await
or let!
, the logical control flow is blocked, so we don't really care if the
operation continues running on the current thread or somewhere else. We cannot do anything
until it completes anyway. However, when we start an asynchronous operation just by running a
Task
(in C#) or by calling one of the Async.Foo
method (in F#), we
need to know how exactly is the operation going to be started. The reason is that we may want to
do something afterwards. There are some other tricky aspects - for example, we want to access GUI
controls only from the main GUI thread, but I'll discuss that in some other article.
Starting behavior in C#
Let's start by looking at the behavior implemented by the C# asynchronous programming model. Here is a quote from the "The Task-based Asynchronous Pattern" document distributed with the CTP:
When called, an asynchronous method synchronously executes the body of the function up until the first await expression, at which point it returns. (...) After the execution of the first await expression, the remaining body of the method effectively gets executed concurrently with the consuming code that called the asynchronous method.
This means that all code until the first await
will run on the thread where the
operation is started. This is something that you should be careful about, because it is quite
easy to accidentally write code that is supposed to run in parallel, but does not. The following
trivial example ilustrates the problem:
// Asynchronous operation that performs long-running
// synchronous work before the first use of 'await'
async Task LongStartingOperation(string name) {
Console.WriteLine("Starting {0}", name);
Thread.Sleep(1000);
Console.WriteLine("Completed {0}", name);
await TaskEx.Yield();
}
// Start the operation twice as a task
var t1 = LongStartingOperation("first");
var t2 = LongStartingOperation("second");
If you run this code snippet, you'll see that the code runs fully synchronously. It first
runs the body of the first operation, until it reaches await
and only then (after
one second) starts the second operation. This means that the long synchronous work (the call
to Sleep
) is not parallelized.
The key point here is the await TaskEx.Yield()
expression. This expression waits
for an operation that essentially doesn't do anything. It just reschedules the rest of the
body to be executed later in the current synchronization context. This means that when the
method is running in a thread pool, the rest of the method will be scheduled as a new work item
and the method will stop blocking the current thread. To fix the example above, all we need to
do is to move the use of Yield
to the first line of the method.
Starting behavior in F#
In F#, the asynchronous workflow is not started automatically. Instead, we can use one of several
methods to start the workflow, so we can choose what behavior we want. The methods are static
methods of the Async
type and are documented in MSDN [2].
The following F# snippet shows a function longStartingOperation
which is written as
an asynchronous workflow, but doesn't contain any asynchronous call. It shows two ways of calling it
- in the first case the two operations will run in parallel and the second case behaves similarly to
the C# example:
1: // Asynchronous operation that performs long-running 2: // synchronous work before the first use of 'await' 3: let longStartingOperation name = async { 4: printfn "Starting %s" name 5: Thread.Sleep(1000) 6: printfn "Completed %s" name } 7: 8: // Starts the operation twice in parallel 9: do longStartingOperation "first" |> Async.Start 10: longStartingOperation "second" |> Async.Start 11: 12: // Runs first part of the operation synchrously (like C#) 13: do longStartingOperation "first" |> Async.StartImmediate 14: longStartingOperation "second" |> Async.StartImmediateF# Web Snippets
The Async.Start
method runs the specified workflow in a thread pool, which means
that it just schedules the work and returns immediately before any part of the workflow is executed.
When there is an asynchronous call in the workflow started using Async.Start
, the body is
started on the current thread. This is initially the thread-pool thread, but if the operation
completes on another thread (e.g. the GUI thread), the workflow will continue running on
that thread.
The method Async.StartImmediate
behaves just like C# tasks. It runs the first
block of the workflow on the current thread and then schedules the rest of the code to be executed
on the current synchronization context (which may be a thread pool, GUI thread, etc.) This method
also ensures that all subsequent code in the workflow is executed in the same synchronization context
If the synchronization context represents just a single thread (for example the GUI thread), then the
whole workflow will run just on this single thread and will interleave with other workflows.
The second case is exactly the behavior of C# tasks. You can configure some properties of the
C# control flow using the TaskEx.ConfigureAwait
method. Most notably, it allows you
to disable returning to the same synchronization context after the first asynchronous operation
completes. However, you can't change the fact that the first block of code runs synchronously
(because this is just not possible in a hot task model).
Summary
In this article, I discussed some of the differences between the C# and F# asynchronous programming
model. The first difference is that F# asynchronous workflows automatically pass around
CancellationToken
object, which means that they support cancellation without any
additional work. In C#, we need to pass the token explicitly. To follow the .NET coding guidelines,
the token should be passed as the last parameter named cancellationToken
. When
implementing cancellation, you need to repeatedly check whether a cancellation was requested, which
can be done for example using ThrowIfCancellationRequested
.
Another difference that we looked at is related to the control flow. I introduced three models for asynchronous programming. In the hot tasks model used in C#, the asynchronous method returns a task that is already running, while in the task generator model used in F#, the result of asynchronous workflow can be used to build new tasks. The third model named cold tasks would return a task that is not started. We also discussed some more subtle aspects of starting asynchronous task from a synchronous code.
Last but not least, the implementations of asynchronous programming model in C# and F# compiler are also quite different, but I'll talk about this in the next article...
References
- [1] The F# Asynchronous Programming Model - Don Syme, Tomas Petricek, Dmitry Lomov
- [2] Control.Async class (F#) - MSDN documentation
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
// Asynchronously get response and dispose it when we're done
use! response = request.AsyncGetResponse()
use stream = response.GetResponseStream()
let temp = new MemoryStream()
let buffer = Array.zeroCreate 4096
type: int
implements: IComparable
implements: IFormattable
implements: IConvertible
implements: IComparable<int>
implements: IEquatable<int>
inherits: ValueType
type: Stream
implements: IDisposable
inherits: MarshalByRefObject
member Stream.AsyncRead : count:int -> Async<byte []>
member Stream.AsyncRead : buffer:byte [] * ?offset:int * ?count:int -> Async<int>
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
type: MemoryStream
implements: IDisposable
inherits: Stream
inherits: MarshalByRefObject
let html = (new StreamReader(temp)).ReadToEnd()
Full name: Untitled.regTitle
type: Regex
implements: Runtime.Serialization.ISerializable
Regex.Match(input: string) : Match
Regex.Match(input: string, startat: int) : Match
Regex.Match(input: string, beginning: int, length: int) : Match
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
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
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.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)
type: CancellationTokenSource
implements: IDisposable
class
interface IDisposable
new : unit -> CancellationTokenSource
member Cancel : unit -> unit
member Dispose : unit -> unit
member Token : CancellationToken
static member CreateLinkedTokenSource : token1:CancellationToken * token2:CancellationToken -> CancellationTokenSource
end
Full name: System.Threading.CancellationTokenSource
type: CancellationTokenSource
implements: IDisposable
Full name: Microsoft.FSharp.Core.Operators.ignore
Download page content and return the title
Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
type: WebClient
implements: ComponentModel.IComponent
implements: IDisposable
inherits: ComponentModel.Component
inherits: MarshalByRefObject
class
inherit System.ComponentModel.Component
new : unit -> System.Net.WebClient
member BaseAddress : string with get, set
member CachePolicy : System.Net.Cache.RequestCachePolicy with get, set
member CancelAsync : unit -> unit
member Credentials : System.Net.ICredentials with get, set
member DownloadData : string -> System.Byte []
member DownloadData : System.Uri -> System.Byte []
member DownloadDataAsync : System.Uri -> unit
member DownloadDataAsync : System.Uri * obj -> unit
member DownloadFile : string * string -> unit
member DownloadFile : System.Uri * string -> unit
member DownloadFileAsync : System.Uri * string -> unit
member DownloadFileAsync : System.Uri * string * obj -> unit
member DownloadString : string -> string
member DownloadString : System.Uri -> string
member DownloadStringAsync : System.Uri -> unit
member DownloadStringAsync : System.Uri * obj -> unit
member Encoding : System.Text.Encoding with get, set
member Headers : System.Net.WebHeaderCollection with get, set
member IsBusy : bool
member OpenRead : string -> System.IO.Stream
member OpenRead : System.Uri -> System.IO.Stream
member OpenReadAsync : System.Uri -> unit
member OpenReadAsync : System.Uri * obj -> unit
member OpenWrite : string -> System.IO.Stream
member OpenWrite : System.Uri -> System.IO.Stream
member OpenWrite : string * string -> System.IO.Stream
member OpenWrite : System.Uri * string -> System.IO.Stream
member OpenWriteAsync : System.Uri -> unit
member OpenWriteAsync : System.Uri * string -> unit
member OpenWriteAsync : System.Uri * string * obj -> unit
member Proxy : System.Net.IWebProxy with get, set
member QueryString : System.Collections.Specialized.NameValueCollection with get, set
member ResponseHeaders : System.Net.WebHeaderCollection
member UploadData : string * System.Byte [] -> System.Byte []
member UploadData : System.Uri * System.Byte [] -> System.Byte []
member UploadData : string * string * System.Byte [] -> System.Byte []
member UploadData : System.Uri * string * System.Byte [] -> System.Byte []
member UploadDataAsync : System.Uri * System.Byte [] -> unit
member UploadDataAsync : System.Uri * string * System.Byte [] -> unit
member UploadDataAsync : System.Uri * string * System.Byte [] * obj -> unit
member UploadFile : string * string -> System.Byte []
member UploadFile : System.Uri * string -> System.Byte []
member UploadFile : string * string * string -> System.Byte []
member UploadFile : System.Uri * string * string -> System.Byte []
member UploadFileAsync : System.Uri * string -> unit
member UploadFileAsync : System.Uri * string * string -> unit
member UploadFileAsync : System.Uri * string * string * obj -> unit
member UploadString : string * string -> string
member UploadString : System.Uri * string -> string
member UploadString : string * string * string -> string
member UploadString : System.Uri * string * string -> string
member UploadStringAsync : System.Uri * string -> unit
member UploadStringAsync : System.Uri * string * string -> unit
member UploadStringAsync : System.Uri * string * string * obj -> unit
member UploadValues : string * System.Collections.Specialized.NameValueCollection -> System.Byte []
member UploadValues : System.Uri * System.Collections.Specialized.NameValueCollection -> System.Byte []
member UploadValues : string * string * System.Collections.Specialized.NameValueCollection -> System.Byte []
member UploadValues : System.Uri * string * System.Collections.Specialized.NameValueCollection -> System.Byte []
member UploadValuesAsync : System.Uri * System.Collections.Specialized.NameValueCollection -> unit
member UploadValuesAsync : System.Uri * string * System.Collections.Specialized.NameValueCollection -> unit
member UploadValuesAsync : System.Uri * string * System.Collections.Specialized.NameValueCollection * obj -> unit
member UseDefaultCredentials : bool with get, set
end
Full name: System.Net.WebClient
type: WebClient
implements: ComponentModel.IComponent
implements: IDisposable
inherits: ComponentModel.Component
inherits: MarshalByRefObject
class
new : string -> System.Uri
new : string * bool -> System.Uri
new : string * System.UriKind -> System.Uri
new : System.Uri * string -> System.Uri
new : System.Uri * string * bool -> System.Uri
new : System.Uri * System.Uri -> System.Uri
member AbsolutePath : string
member AbsoluteUri : string
member Authority : string
member DnsSafeHost : string
member Equals : obj -> bool
member Fragment : string
member GetComponents : System.UriComponents * System.UriFormat -> string
member GetHashCode : unit -> int
member GetLeftPart : System.UriPartial -> string
member Host : string
member HostNameType : System.UriHostNameType
member IsAbsoluteUri : bool
member IsBaseOf : System.Uri -> bool
member IsDefaultPort : bool
member IsFile : bool
member IsLoopback : bool
member IsUnc : bool
member IsWellFormedOriginalString : unit -> bool
member LocalPath : string
member MakeRelative : System.Uri -> string
member MakeRelativeUri : System.Uri -> System.Uri
member OriginalString : string
member PathAndQuery : string
member Port : int
member Query : string
member Scheme : string
member Segments : string []
member ToString : unit -> string
member UserEscaped : bool
member UserInfo : string
static val UriSchemeFile : string
static val UriSchemeFtp : string
static val UriSchemeGopher : string
static val UriSchemeHttp : string
static val UriSchemeHttps : string
static val UriSchemeMailto : string
static val UriSchemeNews : string
static val UriSchemeNntp : string
static val UriSchemeNetTcp : string
static val UriSchemeNetPipe : string
static val SchemeDelimiter : string
static member CheckHostName : string -> System.UriHostNameType
static member CheckSchemeName : string -> bool
static member Compare : System.Uri * System.Uri * System.UriComponents * System.UriFormat * System.StringComparison -> int
static member EscapeDataString : string -> string
static member EscapeUriString : string -> string
static member FromHex : char -> int
static member HexEscape : char -> string
static member HexUnescape : string * int -> char
static member IsHexDigit : char -> bool
static member IsHexEncoding : string * int -> bool
static member IsWellFormedUriString : string * System.UriKind -> bool
static member TryCreate : string * System.UriKind * System.Uri -> bool
static member TryCreate : System.Uri * string * System.Uri -> bool
static member TryCreate : System.Uri * System.Uri * System.Uri -> bool
static member UnescapeDataString : string -> string
end
Full name: System.Uri
type: Uri
implements: Runtime.Serialization.ISerializable
Prints title of a page. Uses a backup server if the
main server is not available at the moment
type: bool
implements: IComparable
implements: IConvertible
implements: IComparable<bool>
implements: IEquatable<bool>
inherits: ValueType
Should the application use main server or a backup server?
type: string
implements: IComparable
implements: ICloneable
implements: IConvertible
implements: IComparable<string>
implements: seq<char>
implements: Collections.IEnumerable
implements: IEquatable<string>
class
inherit System.Runtime.ConstrainedExecution.CriticalFinalizerObject
new : System.Threading.ThreadStart -> System.Threading.Thread
new : System.Threading.ThreadStart * int -> System.Threading.Thread
new : System.Threading.ParameterizedThreadStart -> System.Threading.Thread
new : System.Threading.ParameterizedThreadStart * int -> System.Threading.Thread
member Abort : unit -> unit
member Abort : obj -> unit
member ApartmentState : System.Threading.ApartmentState with get, set
member CurrentCulture : System.Globalization.CultureInfo with get, set
member CurrentUICulture : System.Globalization.CultureInfo with get, set
member ExecutionContext : System.Threading.ExecutionContext
member GetApartmentState : unit -> System.Threading.ApartmentState
member GetCompressedStack : unit -> System.Threading.CompressedStack
member GetHashCode : unit -> int
member Interrupt : unit -> unit
member IsAlive : bool
member IsBackground : bool with get, set
member IsThreadPoolThread : bool
member Join : unit -> unit
member Join : int -> bool
member Join : System.TimeSpan -> bool
member ManagedThreadId : int
member Name : string with get, set
member Priority : System.Threading.ThreadPriority with get, set
member Resume : unit -> unit
member SetApartmentState : System.Threading.ApartmentState -> unit
member SetCompressedStack : System.Threading.CompressedStack -> unit
member Start : unit -> unit
member Start : obj -> unit
member Suspend : unit -> unit
member ThreadState : System.Threading.ThreadState
member TrySetApartmentState : System.Threading.ApartmentState -> bool
static member AllocateDataSlot : unit -> System.LocalDataStoreSlot
static member AllocateNamedDataSlot : string -> System.LocalDataStoreSlot
static member BeginCriticalRegion : unit -> unit
static member BeginThreadAffinity : unit -> unit
static member CurrentContext : System.Runtime.Remoting.Contexts.Context
static member CurrentPrincipal : System.Security.Principal.IPrincipal with get, set
static member CurrentThread : System.Threading.Thread
static member EndCriticalRegion : unit -> unit
static member EndThreadAffinity : unit -> unit
static member FreeNamedDataSlot : string -> unit
static member GetData : System.LocalDataStoreSlot -> obj
static member GetDomain : unit -> System.AppDomain
static member GetDomainID : unit -> int
static member GetNamedDataSlot : string -> System.LocalDataStoreSlot
static member MemoryBarrier : unit -> unit
static member ResetAbort : unit -> unit
static member SetData : System.LocalDataStoreSlot * obj -> unit
static member Sleep : int -> unit
static member Sleep : System.TimeSpan -> unit
static member SpinWait : int -> unit
static member VolatileRead : System.Byte -> System.Byte
static member VolatileRead : int16 -> int16
static member VolatileRead : int -> int
static member VolatileRead : int64 -> int64
static member VolatileRead : System.SByte -> System.SByte
static member VolatileRead : uint16 -> uint16
static member VolatileRead : uint32 -> uint32
static member VolatileRead : System.IntPtr -> System.IntPtr
static member VolatileRead : System.UIntPtr -> System.UIntPtr
static member VolatileRead : uint64 -> uint64
static member VolatileRead : float32 -> float32
static member VolatileRead : float -> float
static member VolatileRead : obj -> obj
static member VolatileWrite : System.Byte * System.Byte -> unit
static member VolatileWrite : int16 * int16 -> unit
static member VolatileWrite : int * int -> unit
static member VolatileWrite : int64 * int64 -> unit
static member VolatileWrite : System.SByte * System.SByte -> unit
static member VolatileWrite : uint16 * uint16 -> unit
static member VolatileWrite : uint32 * uint32 -> unit
static member VolatileWrite : System.IntPtr * System.IntPtr -> unit
static member VolatileWrite : System.UIntPtr * System.UIntPtr -> unit
static member VolatileWrite : uint64 * uint64 -> unit
static member VolatileWrite : float32 * float32 -> unit
static member VolatileWrite : float * float -> unit
static member VolatileWrite : obj * obj -> unit
end
Full name: System.Threading.Thread
type: Thread
implements: Runtime.InteropServices._Thread
inherits: Runtime.ConstrainedExecution.CriticalFinalizerObject
Thread.Sleep(timeout: TimeSpan) : unit
Thread.Sleep(millisecondsTimeout: int) : unit
Published: Monday, 1 November 2010, 4:36 PM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: c#, asynchronous, f#