LA.NET [EN]

Jun 20

In the previous posts, we’ve seen how to implement the asynchronous event based pattern and how to support several features:

Today we’re going to update our initial code to support several simultaneous asynchronous calls of the same method. If you look at that code, you’ll notice that for supporting a single operation, we’re using several fields:

  • an AsyncOperation (_currentOperation) instance that represents the current operation;
  • a boolean field (_isRunning) which indicates if there’s currently a running operation;
  • a boolean field used for supporting cancellation.

Going multi-method is not really complicated, though it means we need to pay more attention to the way the internal fields of the class are accessed. The first thing we’ll need is a container for storing several items. You probably recall from previous posts that we mentioned an optional Object parameter for several of the methods exposed by a class. This optional parameter stops being optional when we want to support several asynchronous operations. In practice, it’s used as a key that identifies a specific asynchronous execution. Notice that the consumer of the API must remember that key if it wants to get progress info or if it needs to cancel a running operation.  In this case, we’ll be using a Dictionary<Object, AsyncOperation> as the container that stores all the running asynchronous operations.

Using a dictionary means that will have to translate the operations that relied on the fields into methods that use the dictionary. Here are the methods we’ve ended up adding to the previous class:

private IDictionary<Object, AsyncOperation> _operations =
                  new Dictionary<Object, AsyncOperation>();
private void AddOperation(Object key, AsyncOperation operation) {
  lock (_operations) {
    if (_operations.ContainsKey(key)) {
      throw new ArgumentException();
    }
    _operations.Add(key, operation);
  }
}
private AsyncOperation GetOperation(Object key) {
  AsyncOperation operation = null;
  lock (_operations) {
    operation = _operations[key];
  }
  return operation;
}
private void CancelOperation(Object key) {
  lock (_operations) {
    _operations[key] = null;
  }
}
private void EndOperation(Object key) {
  lock (_operations) {
    _operations.Remove(key);
  }
}
private Boolean IsRunning(Object key) {
  var isRunning = false;
  lock (_operations) {
    isRunning = _operations.ContainsKey(key) &&
                _operations[key] != null;
  }
  return isRunning;
}
private Boolean OperationWasCancelled(Object key) {
  Boolean cancelled = false;
  lock (_operations) {
    cancelled = _operations.ContainsKey(key) &&
                _operations[key] == null;
  }
  return cancelled;
}



As you can see,we’ve added several methods that lets cancel an operation (CancelOperation),check if an operation is running (IsRunning), check if an operation was cancelled (OperationWasCancelled) and clear an operation which has ended from the private dictionary (EndOperation). Notice that we’re using locks for ensuring proper dictionary access.



There are a couple of interesting changes between the previous snippet and the old code:



  • the first thing you should notice is that the asynchronous operations can only be identified by a key (we’ll see from where that key comes in the next paragraphs) and that’s why all the helper methods receive one.
  • if you compare the “running” concept implementation with the one we have in the previous snippet, you should notice that running means having a valid entry with the specified key (in the previous simpler code, we used a simple field for checking if a task was running);
  • cancelling means that we set the associated AsynchronousOperation to null but don’t remove the entry from the dictionary;
  • we’ve also got a helper method that should only be called when the asynchronous operation ends and is responsible for removing everything from the entry from the dictionary.


The next thing we need to change is the IsPrimeAsync method. Besides adding the Object parameter used for identifying an asynchronous operation, we also need to change the internal code used by that method:



public void IsPrimeAsync(Int32 number, Object key) {
  if (IsRunning(key)) {
      throw new InvalidOperationException();
  }
  var currentOperation = AsyncOperationManager.CreateOperation(null);
  AddOperation(key, currentOperation);
  ThreadPool.QueueUserWorkItem(state =>  {
    var numberToCheck = (Int32)number;
    var isPrime = false;
    Exception throwException = null;
    try {
      if (number > 2) {
        isPrime = true;
        var half = number / 2;
        var currentProgress = 0f;
        for (var i = 2; i < half &&
                  !OperationWasCancelled(key); i++) {
            if (number % i == 0) {
                isPrime = false;
                break;
            }
            currentProgress = ((float)i  / (float)half) * 100;
            currentOperation.Post(
              evtState => OnProgressChanged(
                  (ProgressChangedEventArgs)evtState),
                  new ProgressChangedEventArgs(
                    (Int32)currentProgress, key));
          }
      }
    }
    catch (Exception ex) {
        throwException = ex;
    }
    finally {
        NotifyEndOfOperation(
          numberToCheck,
          isPrime,
          OperationWasCancelled(key),
          throwException,
          key,
          currentOperation);
    }
  }, number);
}



I think there’s not much to say about this method: the principles are the same as before but now everything is encapsulated by several helper methods. As you can see, we’ve updated the code use for propagating progress (now we pass the key that identifies the operation, instead of passing null) and we’ve also changed the API of the NotifyEndOfOperation so that it also receives the associated AsyncOperation instance and its key (we could always get the key from the UserSuppliedState property of the AsyncOperation instance, but I preferred to be explicit about it). Here’s the code for that method:



private void NotifyEndOfOperation( Int32 numberToTest,
  Boolean isPrime,
  Boolean cancelled,
  Exception thrownException,
  Object key,
  AsyncOperation currentOperation ){
    var evt = new PrimeNumberVerificationCompletedEventArgs(
            numberToTest, isPrime, thrownException, cancelled, key);
    currentOperation.PostOperationCompleted(
          evtState => {
            EndOperation(key);
            OnPrimeNumberCompleted(
              (PrimeNumberVerificationCompletedEventArgs)evtState);
         },
        evt);
}



The main point of interest here is that we’ve updated the instantiation of the PrimeNumberVerificationCompletedEventArgs so that if is able to flow the key that identifies the operation that ended. Notice also that we clear the current AsyncOperation from the dictionary before notifying the user of the completion of the task.



The only thing needed is updating the code used for cancelling a running task:



public void CancelAsync(Object key) {
  if (OperationWasCancelled(key)) {
      return;
  }
  CancelOperation(key);
}



And that’s it. With this post, I’d say that there really isn’t much more to say about this pattern. Keep tuned for more on multithreading apps!

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>