Question #5
Here you have a very simple implementation of Template method pattern.
I think that the developer who created Algorithm2 doesn't understand well how async/await keywords work. The main problem is that finally block inside Start method will be executed before DoAsyncCalculations method will end calculations. In other words resources will be disposed in the middle of calculations and this will cause an exception. Sequence of events will be as follows:
Here you have a very simple implementation of Template method pattern.
public abstract class BaseAlgorithm { protected SomeObject Resource { get; set; } //Other resources public void Start() { // Configure Resource = new SomeObject(); //... try { InnerStart(); } finally { // Clean up Resource.Dispose(); Resource= null; //... } } protected abstract void InnerStart(); } public class Algorithm1: BaseAlgorithm { protected override void InnerStart() { //Do something with allocated resources } }At some point someone decided to create a new class Algorithm2 derived from BaseAlgorithm. The difference between the new class and the previous one is that Algorithm2 starts an asynchronous operation. A programmer decided to use async/await keywords to handle this scenario. What do you think about this approach? What could possibly go wrong?
public class Algorithm2: BaseAlgorithm { protected async override void InnerStart() { var task = DoAsyncCalculations(); await task; //Do something with allocated resources } private Task DoAsyncCalculations() { //Let's simulate asynchronous operation return Task.Factory.StartNew(() => Thread.Sleep(1000)); } }Answer #5
I think that the developer who created Algorithm2 doesn't understand well how async/await keywords work. The main problem is that finally block inside Start method will be executed before DoAsyncCalculations method will end calculations. In other words resources will be disposed in the middle of calculations and this will cause an exception. Sequence of events will be as follows:
- Start method begins.
- SomeObject is created.
- InnerStart method begins.
- InnerStart method starts an asynchronous operation and uses await to suspend its progress.
- This causes that control returns to Start method.
- Start method cleanups resources.
- When the asynchronous operation is finished InnerStart method continues processing. It tries to use resources, that have been already disposed, what leads to an exception.
It is also not recommended to have async void methods (except event handlers). If an async method doesn't return a task it cannot be awaited. It is also easier to handle exceptions if an async method returns a task. For details see also this article.
To fix a problem BaseAlgorithm must be aware of asynchronous nature of calculations. For example InnerStart method can return a task which will be awaited inside try block. However, it also means that synchronous version of InnerStart method in Algorithm1 will have to be changed. It may not be acceptable. Generally, providing asynchronous wrappers for synchronous methods is debatable and should be carefully considered.
In this case, I'll consider to have separated implementations of Template method pattern for synchronous and asynchronous algorithms.
To fix a problem BaseAlgorithm must be aware of asynchronous nature of calculations. For example InnerStart method can return a task which will be awaited inside try block. However, it also means that synchronous version of InnerStart method in Algorithm1 will have to be changed. It may not be acceptable. Generally, providing asynchronous wrappers for synchronous methods is debatable and should be carefully considered.
In this case, I'll consider to have separated implementations of Template method pattern for synchronous and asynchronous algorithms.