Thread Helpers

| | Comments (7)

Yesterday we learned all about the APIs on the Thread class itself. Today we're going to talk about threading functions that are external to threads.

First, there's App.CurrentThread. As the name implies, this property tells you which thread is the one that's currently executing. However, there's a catch. We do not give you access to the main thread in REALbasic because it's way too easy to shoot yourself in the foot (or the head!), so if the main thread is the one that's currently executing, this property will be Nil. I should note that this behavior isn't actually harmful because you are able to set thread priorities relative to one another. So if you want to reduce the main thread's priority, you can do so by jacking the other thread priorities up. This property is currently read-only.

Now we'll go on to talk about App.SleepCurrentThread. This handy little method allows you to generically put a thread to sleep for a certain period of time. Whenever you're looking for a Wait function, this is the one you'd use (however, I'd argue that you should almost never use a Wait function). It lets you specify the number of milliseconds to put the thread to sleep for. So why wouldn't you use this instead of Thread.Sleep? Simple -- Thread.Sleep is better! It lets you flag whether you want the thread to be woken up early or not. This API doesn't allow you to do that, so the thread is sleeping no matter what.

Finally, we get to App.YieldToNextThread, which is a very handy API. It lets you decide when to manually trigger a context switch into the next thread
that the thread scheduler picks. Remember how Thread.Resume doesn't trigger a context switch? Well, here's a way for you to trigger that switch manually:
[rbcode]
Thread1.Resume
App.YieldToNextThread[/rbcode]

However, you should keep in mind that it's the thread scheduler that picks which thread to run next, so if you have multiple threads, it may not wake Thread1 up (it may pick ThreadX instead).

This API is even more useful if you've got a thread (including the main thread) where you're pretty sure you're done for this time slice. You can either put yourself to sleep (with App.SleepCurrentThread, or Thread.Sleep), or if you're pretty sure that by the time your next time slice arrives, there'll be more work to do, you can just yield your time slice up by calling App.YieldToNextThread.

At this juncture, I'd like to point out an interesting tidbit of information. Calling the Thread.Run method increments the reference count to the thread. What does this mean for you? It means you can write code like this:
[rbcode]
Sub SomeSpiffyMethod()
Dim t as new MySpiffyThread
t.Run
End Sub[/rbcode]

And it will "just work". Usually, upon leaving the method, all local variables are destroyed because they only have a single reference to them, which is decremented when leaving the method. However, because t's reference count = 2 (one for the local, one from Run), it is not destroyed and continues to run.

Now, let's talk about the deadly App.DoEvents method. When dealing with threads -- don't use it. Period, end of story. In RB2005r2, we made it so that calling App.DoEvents from within a thread no longer blows up in your face (it simply does an App.YieldToNextThread instead of running a second iteration of the event loop within your thread's context), however, you're still probably using it for all the wrong reasons -- so just don't use it. That's an easy API to discuss. ;-)

That concludes all the thread helper APIs and what they're used for. In the future, we'll get into how to keep your data thread safe by using locking mechanisms.

7 Comments

Just curious, and I know this has been brought up before, but...

Is RB 2005 using App.DoEvents anywhere? I've been seeing some really weird crashes on compile intermittently, and I was thinking that the temptation for using App.DoEvents once or twice during the compile cycle must be pretty strong (since otherwise it locks up the UI so much).

If you guys are using it on the sly, perhaps that's causing my intermittent, un-reproducible compile crashes? I can provide a log if you want, but it didn't help RS support.

I've seen us use App.DoEvents a very minor amount (only once or twice that I can think of -- and it's to force an immediate update of some state), but we're not using it anywhere near the compiler. The compile process is a threaded process, so that's why it doesn't lock the UI.

If you've got a crash log, it may be interesting to see (though I make no promises). You can email it to me if you'd like.

Hey Aaron,

Just a quick question/observation about App.DoEvents. There have been a few area where I couldn't use a thread but still needed the screen to refresh, as long as I don't get carried away. Do you feel that is an acceptable use?

Second, my app was interfering with the running of other apps on a windows machine before I peppered it with app.doevents (something I didn't want to do BTW). Which I foond odd since it spends most of its time setting in the GUI waiting for user events. This was when it was compiled in 5.5, I haven't tried removing them in 2005 yet... haven't had the time.

Any suggestions?

Thanks,

Kevin (serKevin from RealGurus)

Done. Thanks!

@Kevin -- Generally speaking, no. App.DoEvents is a way for you to yield time back to the OS in single-threaded console applications. Personally, I think that bringing it into GUI applications was a mistake.

As for the other issue, I have no clue. What do you mean by "interfering"? I can't imagine why DoEvents would change the behavior of *other* applications..

If you're taking requests, I'd love to know the differences between Semaphores and CriticalSections, and where it is appropriate to use each.

Right now, I'm using Semaphores to protect properties and shared resources which are accessed by multiple threads.

I am pretty sure that's going to be tomorrow's posting, assuming I get the chance to write it. ;-)

Leave a comment

Disclaimer

I'm currently an employee of REAL Software. My blog is mine. The opinions represented in this blog are mine as well and may not represent my employer's opinions. All original material is copyrighted and property of the author.

REALbasic® is a registered trademark of REAL Software, Inc. REAL SQL Server™ and Lingua™ are pending trademarks of REAL Software, Inc. All rights reserved.