I had an entire post all written up about nice ways to handle data synchronization with threads in REALbasic. But then hell broke loose and I managed to lose the entire post. Gotta love that!
In any event, here's a half-hearted attempt at explaining things in a very quick and dirty manner. However, I'll make up for the lack of detail in the post by giving a sample project that demonstrates instead.
Problem:
It's hard to keep track of what semaphores go with which resource and things of that nature. It's usually quite easy to forget about the semaphore altogether and just access the data directly. And almost every data protection scheme you can come up with won't work in a generic manner because each resource and situation is slightly different.
Solution:
Make an easy way for users to synchronize data. Yes, it still requires the user to remember that they need to care. That's a very tough problem to solve though. We're just going to try to make it easy for users to do and hope they remember to do it.
So how easy do we want this to be? Simple -- two calls to take care of everything.
Essentially, we want the user to be able to say:
Sync( some data item )
Do some things
Do some more things
This takes a long time
End Sync( some data item )This way it's very simple for them to use -- it neatly blocks off what needs to be synchronized and there's no tracking that the programmer really needs to do.
So we're going to make a module that let's the user do just that! We're going to use a Dictionary to store the locking mechanism of choice (which is a Semaphore), where the keys in the dictionary is just the object which we wish to synchronize (and the value is the semaphore itself). This works out really nicely because the Dictionary will store unique semaphore for unique items. So if you are using the same object to sync, then you will be getting the same semaphore signalled. If you're using different objects, then you get different semaphores (which makes sense because the objects are different references).
[rbcode]
Protected Sub Lock(v as Variant)
// The user wants to lock access to the
// data passed in. So the first thing we
// need to do is see whether our Dictionary
// already has a semaphore for this data.
if mResources = nil then mResources = new Dictionary
if mResources.HasKey( v ) then
// We already have this key in the dictionary. So
// we want to access the value, which is a semaphore
// object.
dim sem as Semaphore = mResources.Value( v )
// Now we want to signal the semaphore. This
// will cause the thread to block if some other
// thread has already accessed this same variant.
sem.Signal
// Now the thread can access the resource, so
// we're done here.
return
end if
// If we got here, then there's no key for
// this Variant. So make one!
dim sem as new Semaphore
mResources.Value( v ) = sem
// And lock it
sem.Signal
// Now we're done
End Sub
Protected Sub EndLock(v as Variant)
// The user is done accessing
// this particular resource, so we
// need to dig it out of the Dictionary
// and release it
// Sanity check #1
if mResources = nil then
dim exp as new RuntimeException
exp.Message = "You have called EndLock before ever calling Lock"
raise exp
return
end if
// Sanity check #2
if not mResources.HasKey( v ) then
dim exp as new RuntimeException
exp.Message = "You have called EndLock before ever calling Lock for this particular object"
raise exp
return
end if
// Now we know that we've got the
// semaphore in our dictionary, so
// grab it out and release it.
dim sem as Semaphore = mResources.Value( v )
sem.Release
// We're done!
End Sub[/rbcode]
Ta da! That's all these is to it. When you want to use this code, it looks very similar to what we outlined before:[rbcode]
Sync.Lock( someDataItem )
DoSomeThings( someDataItem )
DoSomeMoreThings( someDataItem )
someDataItem.ThisTakesALongTime
Sync.EndLock( someDataItem )[/rbcode]
Some things to note about this method though -- it's still possible to get your locks out of sync. If you call Sync.Lock and forget to call Sync.EndLock, then you are really going to be hosed. You can run into this situation pretty easily by accidentally calling Return from the middle of your sync'ed operation.
However, you're also no worse off than you would be if you used Semaphores directly. This is just an easier mechanism since you don't need to bother keeping track of the semaphores at all.
Sorry for the total lack of decent descriptions and information, but I don't have the time to rewrite the hour's worth of work I just lost. :-P
Oh, this is nifty. I'll have to look into it for my projects. I think I might have problems with data synchronization...
A couple of weeks ago, I only ever used Mutexes, now you're turning me into a Semaphore junkie.
It's getting just like the old days on Unix ;)
Well, the nice thing about Semaphores is that they really make sense when dealing with resources. Mutexes and their parent (the CriticalSection) also do good protection, but they don't protect a thread from itself (because they can be re-entered).
This article at RBLibrary.com covers in a lot more detail the differences between the locking mechanisms and give some very simple examples of their use.
Thanks Aaron, I think I've got a reasonable handle on how Semaphores can be useful now.
Nonetheless I've lashed out 58p on the article you recommend and I'm off to study it.
Have you given any thought to bundling the articles? Ideally it would be nice to have a number of bundling choices that together would cover the entire product line. Like purchasing all from a particular author, or all networking articles, or just the whole FREAKIN wad!
Just a thought...
~joe
@Steve -- if you have questions, you know who to ask!
@Joe -- the thought has crossed my mind, as well as Bill's. We've got some ideas that we've kicked around for various things. But the first order of business is to get the site up and get on a regular updating schedule. Since we have so much RBD content to put up, as well as our exclusive content, we're just focusing on the articles first. Once the content is at a normal flow, then we can focus on packaging stuff (such as accepting payment from places other than PayPal, bundling, etc).
Since PayPal has a minimum flat fee (I think is $0.50), PayPal is taking quite a big percentage from what you charge. Bundling would make sense from just that point alone.
A good bundle would be the whole previous month for some discount like 25%.
@Aaron & Phil: A discount would be nice and 10% would suffice. I was thinking that a bundle would be nice simply as a quick way to grab a bunch of loot! You see I'm a "guy" and so I don't like shopping and filling my basket. I want to grab what I need and go. Every time I occasion your sight I feel like I'm going to grow boobs (hmmm on second thought) as I meander around looking at all of the neat (and cheap) documents.
Please Aaron, I beseech you to allow me to maintain my dignity by bundling together some "volumes" or "compendiums" so that I can avoid the embarrassment of "shopping"...
~joe
Aaron wrote: "If you call Sync.Lock and forget to call Sync.EndLock, then you are really going to be hosed"
Not only a "return" is a problem but an exception as well.
Well, that's exactly what _other_ languages invented the FINALLY block for :)