TP

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#:

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:

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

val downloadPage : (string -> Async<string * int>)

Asynchronously downloads a web page and returns
 title of the page and size in bytes

val url : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
Multiple items
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>
val async : AsyncBuilder

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.async
let request = HttpWebRequest.Create(url)
// 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
val download : (unit -> Async<unit>)
val count : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
val stream : Stream

  type: Stream
  implements: IDisposable
  inherits: MarshalByRefObject
Multiple overloads
member Stream.AsyncRead : count:int -> Async<byte []>
member Stream.AsyncRead : buffer:byte [] * ?offset:int * ?count:int -> Async<int>
val buffer : byte []

  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
property Array.Length: int
val temp : MemoryStream

  type: MemoryStream
  implements: IDisposable
  inherits: Stream
  inherits: MarshalByRefObject
member Stream.AsyncWrite : buffer:byte [] * ?offset:int * ?count:int -> Async<unit>
temp.Seek(0L, SeekOrigin.Begin) |> ignore
let html = (new StreamReader(temp)).ReadToEnd()
val regTitle : Regex

Full name: Untitled.regTitle

  type: Regex
  implements: Runtime.Serialization.ISerializable
Multiple overloads
Regex.Match(input: string) : Match
Regex.Match(input: string, startat: int) : Match
Regex.Match(input: string, beginning: int, length: int) : Match
val html : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
property String.Length: int
val comparePages : Async<unit>

Downloads pages in parallel and prints all results
val results : (string * int) []

  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
type 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
val map : ('T -> 'U) -> 'T [] -> 'U []

Full name: Microsoft.FSharp.Collections.Array.map
Multiple items
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
static member Async.Parallel : computations:seq<Async<'T>> -> Async<'T []>
val title : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
val length : int

  type: int
  implements: IComparable
  implements: IFormattable
  implements: IConvertible
  implements: IComparable<int>
  implements: IEquatable<int>
  inherits: ValueType
type Console =
  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
Multiple overloads
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)
val cts : CancellationTokenSource

  type: CancellationTokenSource
  implements: IDisposable
type CancellationTokenSource =
  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
static member Async.Start : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit
property CancellationTokenSource.Token: CancellationToken
Console.ReadLine() : string
val ignore : 'T -> unit

Full name: Microsoft.FSharp.Core.Operators.ignore
member CancellationTokenSource.Cancel : unit -> unit
val downloadPage : (string -> Async<string>)

Download page content and return the title
val printfn : Printf.TextWriterFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.printfn
val wc : WebClient

  type: WebClient
  implements: ComponentModel.IComponent
  implements: IDisposable
  inherits: ComponentModel.Component
  inherits: MarshalByRefObject
type WebClient =
  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
member WebClient.AsyncDownloadString : address:Uri -> Async<string>
type Uri =
  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
val printTitle : (Async<string> -> Async<string> -> Async<unit>)

Prints title of a page. Uses a backup server if the
 main server is not available at the moment

val main : Async<string>
val backup : Async<string>
val useMainServer : bool

  type: bool
  implements: IComparable
  implements: IConvertible
  implements: IComparable<bool>
  implements: IEquatable<bool>
  inherits: ValueType


Should the application use main server or a backup server?
val longStartingOperation : (string -> Async<unit>)
val name : string

  type: string
  implements: IComparable
  implements: ICloneable
  implements: IConvertible
  implements: IComparable<string>
  implements: seq<char>
  implements: Collections.IEnumerable
  implements: IEquatable<string>
type Thread =
  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
Multiple overloads
Thread.Sleep(timeout: TimeSpan) : unit
Thread.Sleep(millisecondsTimeout: int) : unit
static member Async.StartImmediate : computation:Async<unit> * ?cancellationToken:CancellationToken -> unit

Published: Monday, 1 November 2010, 4:36 PM
Author: Tomas Petricek
Typos: Send me a pull request!
Tags: c#, asynchronous, f#