I'm working on a project in .NET that makes independent calls to both a database and a Web service. I have verified empirically that the database calls are consistently much faster than the Web service. I want to call the Web service in such a way that my main process doesn't have to block waiting for the service to return. It can move on to querying the database and doing some other work, and then check back later to see if the Web service has returned data.
Not only is this my first time doing this in .NET, it's actually the first real concurrent programming I've done in many years. All of my recent programming work has essentially been Web front-ends to database update or select queries. From that point of view, the work tends to be sequential. For example, the code updates a database and then waits for the database to respond so it can present HTML feedback to the user letting them know if the update succeeded or failed. There's not much opportunity for parallelism — at least from my code's point of view. The database in the background might be doing all sorts of fancy multi-tasking, but I don't have to manage it at all from my code.
Since I don't have any recent experience with this stuff, I am influenced by the first multi-tasking language I learned: Ada. Now Ada has this really nice, simple concurrent model built around something called a rendezvous. The idea is that your concurrent tasks have points at which they can come back together to share whatever results or status info they need; this point is called a rendezvous. It's been forever since I wrote in Ada, so I don't remember the exact syntax, but basically, one of your tasks would create a block of code to accept and handle the rendezvous:
accept rendezvous_with_rama (qry: in int; results: out int)
... do some work in here ...end rendezvous_with_rama
Your other task would come to a point where it makes a call into the accept statement of the first task. The task that makes it to that rendezvous point first has to wait until the other task gets there as well; this is why it's called a rendezvous — both tasks have to reach the appropriate point in their code before they can exchange data thru the rendezvous entry point. It's not quite as real-time and responsive as the callback mechanisms you might be familiar with in .NET, but for my simple little brain, I find it's a much easier-to-understand model for writing concurrent code.
Without consciously thinking about it, the .NET code I wrote for my concurrent Web service/database calls kind of ended up looking like a rendezvous. Clearly, I was plucking at the patterns I was used to from that long-ago stint as an Ada programmer.
If you've used Visual Studio to do .NET code, you know that when it imports a Web service, it creates a bunch of nice wrapper methods that you can use to create an asynchronous call to the various methods on the Web service. Say, for example, you've got a Web reference called Weather, and it has a method called GetForecast. Visual Studio will create a BeginGetForecast method that initiates a call to GetForecast but returns immediately, so your caller can get on to other business. You can pass that Begin method a callback that gets executed when the Web service is done, but I am still not comfortable in my understanding of how all of this works.
So, after making the Begin call, my code does the other work it has to do (mostly that database call) and then it checks back with the Web service to see if that statement is finished. If not, (just like a rendezvous) my code kind of sits around waiting for the service to finish. The code ended up looking something like this (again, this is not the full syntax — just the flavor):
Dim sqlds as DataSet
Dim websvcds as DataSet
Dim fc as New Weather.Service
Dim asyncResult As IAsyncResult
Dim waitHndl as Threading.WaitHandle
asyncResult = fc.BeginGetForecast("Louisville", Nothing, Nothing)
' Do other work here, like the database call
sqlds = DoDatabaseStuff
'Here is the rendezvous
waitHndl = asyncResult.AsyncWaitHandle
' Set a 100 millisecond timeout
If (asyncResult.IsCompleted) then
websvcds = fc.EndGetForecast(asyncResult)End If
If you're an expert in .NET concurrent programming, please don't flame me for writing lousy code. I know it's bad — it's my first attempt. And, like I said, it's based on patterns I learned from Ada many, many moons ago. At least my approach lets the program do some other work while waiting for the Web service to finish.