Scenarios - Concurrency in C# Cookbook (2014)

Concurrency in C# Cookbook (2014)

Chapter 13. Scenarios

In this chapter, we’ll take a look at a variety of types and techniques to address some common scenarios when writing concurrent programs. These kinds of scenarios could fill up another entire book, so I’ve selected just a few that I’ve found the most useful.

13.1. Initializing Shared Resources

Problem

You have a resource that is shared between multiple parts of your code. This resource needs to be initialized the first time it is accessed.

Solution

The .NET framework includes a type specifically for this purpose: Lazy<T>. You construct an instance of this type with a factory delegate that is used to initialize the instance. The instance is then made available via the Value property. The following code illustrates the Lazy<T> type:

static int _simpleValue;

static readonly Lazy<int> MySharedInteger = new Lazy<int>(() => _simpleValue++);

void UseSharedInteger()

{

int sharedValue = MySharedInteger.Value;

}

No matter how many threads call UseSharedInteger simultaneously, the factory delegate is only executed once, and all threads wait for the same instance. Once it is created, the instance is cached and all future access to the Value property returns the same instance (in the preceding example, MySharedInteger.Value will always be 0).

A very similar approach can be used if the initialization requires asynchronous work; in this case, we use a Lazy<Task<T>>:

static int _simpleValue;

static readonly Lazy<Task<int>> MySharedAsyncInteger =

new Lazy<Task<int>>(async () =>

{

await Task.Delay(TimeSpan.FromSeconds(2)).ConfigureAwait(false);

return _simpleValue++;

});

async Task GetSharedIntegerAsync()

{

int sharedValue = await MySharedAsyncInteger.Value;

}

In this example, the delegate returns a Task<int>, that is, an integer value determined asynchronously. No matter how many parts of the code call Value simultaneously, the Task<int> is only created once and returned to all callers. Each caller then has the option of (asynchronously) waiting until the task completes by passing the task to await.

This is an acceptable pattern, but there is one additional consideration. The asynchronous delegate may be executed on any thread that calls Value, and that delegate will execute within that context. If there are different thread types that may call Value (e.g., a UI thread and a thread-pool thread, or two different ASP.NET request threads), then it may be better to always execute the asynchronous delegate on a thread-pool thread. This is easy enough to do by just wrapping the factory delegate in a call to Task.Run:

static readonly Lazy<Task<int>> MySharedAsyncInteger = new Lazy<Task<int>>(

() => Task.Run(

async () =>

{

await Task.Delay(TimeSpan.FromSeconds(2));

return _simpleValue++;

}));

Discussion

The final code sample is a general code pattern for asynchronous lazy initialization. However, it’s a bit awkward. The AsyncEx library includes an AsyncLazy<T> type that acts just like a Lazy<Task<T>> that executes its factory delegate on the thread pool. It can also be awaited directly, so the declaration and usage look like this:

private static readonly AsyncLazy<int> MySharedAsyncInteger =

new AsyncLazy<int>(async () =>

{

await Task.Delay(TimeSpan.FromSeconds(2));

return _simpleValue++;

});

public async Task UseSharedIntegerAsync()

{

int sharedValue = await MySharedAsyncInteger;

}

TIP

The AsyncLazy<T> type is in the Nito.AsyncEx NuGet package.

See Also

Chapter 1 covers basic async/await programming.

Recipe 12.1 covers scheduling work to the thread pool.

13.2. Rx Deferred Evaluation

Problem

You want to create a new source observable whenever someone subscribes to it. For example, you want each subscription to represent a different request to a web service.

Solution

The Rx library has an operator Observable.Defer, which will execute a delegate each time the observable is subscribed to. This delegate acts as a factory that creates an observable. The following code uses Defer to call an asynchronous method every time someone subscribes to the observable:

static void Main(string[] args)

{

var invokeServerObservable = Observable.Defer(

() => GetValueAsync().ToObservable());

invokeServerObservable.Subscribe(_ => { });

invokeServerObservable.Subscribe(_ => { });

Console.ReadKey();

}

static async Task<int> GetValueAsync()

{

Console.WriteLine("Calling server...");

await Task.Delay(TimeSpan.FromSeconds(2));

Console.WriteLine("Returning result...");

return 13;

}

If you execute this code, you should see output like this:

Calling server...

Calling server...

Returning result...

Returning result...

Discussion

Your own code usually doesn’t subscribe to an observable more than once, but some Rx operators do under the covers. For example, the Observable.While operator will re-subscribe to a source sequence as long as its condition is true. Defer allows you to define an observable that is reevaluated every time a new subscription comes in. This is useful if you need to refresh or update the data for that observable.

See Also

Recipe 7.6 covers wrapping asynchronous methods in observables.

13.3. Asynchronous Data Binding

Problem

You are retrieving data asynchronously and need to data-bind the results (e.g., in the ViewModel of a Model-View-ViewModel design).

Solution

When a property is used in data binding, it must immediately and synchronously return some kind of result. If the actual value needs to be determined asynchronously, you can return a default result and later update the property with the correct value.

Keep in mind that asynchronous operations can usually end with failure as well as success. Since we are writing a ViewModel, we could use data binding to update the UI for an error condition as well.

The AsyncEx library has a type NotifyTaskCompletion that can be used for this:

class MyViewModel

{

public MyViewModel()

{

MyValue = NotifyTaskCompletion.Create(CalculateMyValueAsync());

}

public INotifyTaskCompletion<int> MyValue { get; private set; }

private async Task<int> CalculateMyValueAsync()

{

await Task.Delay(TimeSpan.FromSeconds(10));

return 13;

}

}

It is possible to data-bind to various properties on the INotifyTaskCompletion<T> property, as follows:

<Grid>

<Label Content="Loading..."

Visibility="{Binding MyValue.IsNotCompleted,

Converter={StaticResource BooleanToVisibilityConverter}}"/>

<Label Content="{Binding MyValue.Result}"

Visibility="{Binding MyValue.IsSuccessfullyCompleted,

Converter={StaticResource BooleanToVisibilityConverter}}"/>

<Label Content="An error occurred" Foreground="Red"

Visibility="{Binding MyValue.IsFaulted,

Converter={StaticResource BooleanToVisibilityConverter}}"/>

</Grid>

Discussion

It’s also possible to write your own data-binding wrapper instead of using the one from the AsyncEx library. This code gives the basic idea:

class BindableTask<T> : INotifyPropertyChanged

{

private readonly Task<T> _task;

public BindableTask(Task<T> task)

{

_task = task;

var _ = WatchTaskAsync();

}

private async Task WatchTaskAsync()

{

try

{

await _task;

}

catch

{

}

OnPropertyChanged("IsNotCompleted");

OnPropertyChanged("IsSuccessfullyCompleted");

OnPropertyChanged("IsFaulted");

OnPropertyChanged("Result");

}

public bool IsNotCompleted { get { return !_task.IsCompleted; } }

public bool IsSuccessfullyCompleted

{ get { return _task.Status == TaskStatus.RanToCompletion; } }

public bool IsFaulted { get { return _task.IsFaulted; } }

public T Result

{ get { return IsSuccessfullyCompleted ? _task.Result : default(T); } }

public event PropertyChangedEventHandler PropertyChanged;

protected virtual void OnPropertyChanged(string propertyName)

{

PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)

handler(this, new PropertyChangedEventArgs(propertyName));

}

}

Note that there is an empty catch clause on purpose: we specifically want to catch all exceptions and handle those conditions via data binding. Also, we explicitly do not want to use ConfigureAwait(false) because the PropertyChanged event should be raised on the UI thread.

TIP

The NotifyTaskCompletion type is in the Nito.AsyncEx NuGet package.

See Also

Chapter 1 covers basic async/await programming.

Recipe 2.7 covers using ConfigureAwait.

13.4. Implicit State

Problem

You have some state variables that need to be accessible at different points in your call stack. For example, you have a current operation identifier that you want to use for logging but that you don’t want to add as a parameter to every method.

Solution

The best solution is to add parameters to your methods, store data as members of a class, or use dependency injection to provide data to the different parts of your code. However, there are some situations where that would overcomplicate the code.

The CallContext type in .NET provides LogicalSetData and LogicalGetData methods that allow you to give your state a name and place it on a logical “context.” When you are done with that state, you can call FreeNamedDataSlot to remove it from the context. The following code shows how to use these methods to set an operation identifier that is later read by a logging method:

void DoLongOperation()

{

var operationId = Guid.NewGuid();

CallContext.LogicalSetData("OperationId", operationId);

DoSomeStepOfOperation();

CallContext.FreeNamedDataSlot("OperationId");

}

void DoSomeStepOfOperation()

{

// Do some logging here.

Trace.WriteLine("In operation: " +

CallContext.LogicalGetData("OperationId"));

}

Discussion

The logical call context can be used with async methods, but only on .NET 4.5 and above. If you try to use it on .NET 4.0 with the Microsoft.Bcl.Async NuGet package, the code will compile but will not work correctly.

You should only store immutable data in the logical call context. If you need to update a data value in the logical call context, then you should overwrite the existing value with another call to LogicalSetData.

The logical call context is not extremely performant. I recommend you add parameters to your methods or store the data as members of a class instead of using the implicit logical call context, if at all possible.

If you are writing an ASP.NET application, consider using HttpContext.Current.Items, which does the same thing but is more performant than CallContext.

See Also

Chapter 1 covers basic async/await programming.

Chapter 8 covers several immutable collections if you need to store complex data as implicit state.