by luisabreu via LA.NET [EN] on 6/17/2009 9:38:35 PM
In the last post of the series, we’ve taken a look at the main features offered by the event based pattern. Today, we’re going to look at how we can implement that pattern. We’re going to start small and we’re going to reuse the IAsyncResult sample for showing how you can implement this pattern.
As we’ve seen in the previous post, we need to (at least) add a method (named XXXAsync) that will start the asynchronous operation and an event (named XXXCompleted) that will fire . In practice, this means that we need something like this:
class DoSomeWork { public Boolean IsPrimeNumber(Int32 number) { /* same as before*/ } public event EventHandler<PrimeNumberVerificationCompletedEventArgs> PrimeNumberCompleted; protected void OnPrimeNumberCompleted( PrimeNumberVerificationCompletedEventArgs evtArgs ){ if (PrimeNumberCompleted != null) { PrimeNumberCompleted(this, evtArgs); } } public void IsPrimeAsync(Int32 number) { //some code here… } }
As you can see, we’ve got a PrimeNumberVerificationCompletedEventArgs that is passed back. As we’ve said, this must be a AsyncCompletedEventArgs (or derived) class. In this case, we need to return a value, so we’ll need to create a new derived class which I called PrimeNumberVerificationCompletedEventArgs. Here’s the code for that class:
public class PrimeNumberVerificationCompletedEventArgs: AsyncCompletedEventArgs { private readonly Int32 _testedNumber; private readonly Boolean _isPrime; internal PrimeNumberVerificationCompletedEventArgs( Int32 testedNumber, Boolean isPrime, Exception exception, Boolean calculationCanceled, Object state ) : base(exception, calculationCanceled, state) { _testedNumber = testedNumber; _isPrime = isPrime; } public Int32 TestedNumber { get{ RaiseExceptionIfNecessary(); return _testedNumber; } } public Boolean IsPrime{ get{ RaiseExceptionIfNecessary(); return _isPrime; } } }
As you can see, I’ve added two properties to the base class: TestedNumber (returns the number that was passed to the IsPrimeAsync method) and IsPrime (returns the result of the processing). As you can see, both properties call the RaiseExceptionIfNecessary method before returning the value back to the client. Internally, this method will throw the exception (when it isn’t null) that supposedly originated during the asynchronous operation and that was passed to the constructor.
As you might expect, most of the action happens on the IsPrimeAsync method. From within that method, we need to start the processing on a different thread. For starters, we’re allowing only one asynchronous call at the time. Here’s a possible implementation for the IsPrimeAsync method:
public void IsPrimeAsync(Int32 number) { if (_isRunning) { throw new InvalidOperationException(); } _isRunning = true; _currentOperation = AsyncOperationManager.CreateOperation(null); ThreadPool.QueueUserWorkItem(state => { var numberToCheck = (Int32)number; var isPrime = false; Exception throwException = null; try { if (number > 2) { isPrime = true; var half = number / 2; for (var i = 2; i < half; i++) { if (number % i == 0) { isPrime = false; break; } } } } catch (Exception ex) { throwException = ex; } finally { NotifyEndOfOperation(numberToCheck, isPrime, false, throwException); }
}, number); }
There are a couple of interesting points here:
The NotifyEndOfOperation method is responsible for “firing“ the event back on the thread that started the request (which is really needed when you’re writing code for GUIs). To do that, it must first pack all the info into a PrimeNumberVerificationEventsArg expected by the consumer of the API. As you’ll see, the method ends up being really simple because we end up relying once again on the “mysterious” AsyncOperation class:
private void NotifyEndOfOperation( Int32 numberToTest, Boolean isPrime, Boolean cancelled, Exception thrownException ){ var evt = new PrimeNumberVerificationCompletedEventArgs( numberToTest, isPrime, thrownException, cancelled, null); _currentOperation.PostOperationCompleted( state => { _isRunning = false; OnPrimeNumberCompleted((PrimeNumberVerificationCompletedEventArgs)state); }, evt); }
As I’ve said before, we’ll spend a couple of posts on GUIs and asynchronous processing. Until then, just know that calling this method signals the end of an asynchronous operation (notice also that this means that you cannot make future calls over this AsyncOperation instance). From within the delegate we pass to the method, we turn off our running flag and fire the event by calling the auxiliary OnPrimeNumberCompleted method:
protected void OnPrimeNumberCompleted( PrimeNumberVerificationCompletedEventArgs evtArgs ){ if (PrimeNumberCompleted != null) { PrimeNumberCompleted(this, evtArgs); } }
And now that we’ve got the class ready, here’s how you’re expected to use it:
var work = new DoSomeWork(); work.PrimeNumberCompleted += (sender, e ) => { Console.WriteLine(e.IsPrime); evt.Set(); }; work.IsPrimeAsync(19);
There still more to say about this pattern, so we’ll return to it in future posts. Keep tuned!
Original Post: Multithreading: implementing the event based pattern
The content of the postings is owned by the respective author. CSharpFeeds is not responsible for the contents of the postings. This site is automatically generated and cannot be reviewed for abusive content. If you find abusive content on CSharpFeeds, please contact us. Designated trademarks and brands are the property of their respective owners. All rights reserved.