Thread overview
Proper way to code multithreaded applications?
Jun 01, 2007
Jason House
Jun 01, 2007
Regan Heath
Jun 01, 2007
Sean Kelly
Jun 01, 2007
Regan Heath
Jun 02, 2007
Jason House
Jun 02, 2007
torhu
Jun 02, 2007
Jason House
Jun 02, 2007
Frank Benoit
Jun 02, 2007
torhu
June 01, 2007
I've started writing a multithreaded application and I'm slowly hitting into different annoyances.  I'm curious if anyone knows the proper way to handle some of these issues.

* must call start to run a thread but thread execution starts with run (If that confuses, which it was intended to do.  Make your own run method for what you want the thread to do but call the start function when you want the thread to begin)
* wait can't be used inside a thread to wait for itself
* yield has no specific timing constraints
* derr is not synchronized.  Debug output from different threads can get multiplexed together.
* msleep isn't available on all platforms (problem inherited from C)
* usleep isn't thread safe (problem inherited from C)

My big issue at the moment is how to cause a thread to delay (approximately) for a period of time that I specify.  I guess I might be ok with yield when the application is in full swing, but I really hate for nearly idle threads to peg the CPU when there's nothing to do.
June 01, 2007
Jason House Wrote:
> I've started writing a multithreaded application and I'm slowly hitting into different annoyances.  I'm curious if anyone knows the proper way to handle some of these issues.
> 
> * must call start to run a thread but thread execution starts with run (If that confuses, which it was intended to do.  Make your own run method for what you want the thread to do but call the start function when you want the thread to begin)

Yep *shrug* those are perhaps bad method names.

> * wait can't be used inside a thread to wait for itself

No, it's intended for when you want to wait for another thread to finish.  You can't wait for yourself to finish because as long as you wait you're not finished.

> * yield has no specific timing constraints

No.. it just calls Sleep(0); (on windows).

> * derr is not synchronized.  Debug output from different threads can get multiplexed together.

You're probably going to have to synchronize it yourself with a mutex or similar.

> * msleep isn't available on all platforms (problem inherited from C)
> * usleep isn't thread safe (problem inherited from C)

Have you heard of nanosleep?

> My big issue at the moment is how to cause a thread to delay (approximately) for a period of time that I specify.  I guess I might be ok with yield when the application is in full swing, but I really hate for nearly idle threads to peg the CPU when there's nothing to do.

I reckon you write an msleep implementation which calls Sleep, msleep, usleep or nanosleep (depending on the platform) and call that.

Regan Heath
June 01, 2007
Jason House wrote:
> I've started writing a multithreaded application and I'm slowly hitting into different annoyances.  I'm curious if anyone knows the proper way to handle some of these issues.
> 
> * must call start to run a thread but thread execution starts with run (If that confuses, which it was intended to do.  Make your own run method for what you want the thread to do but call the start function when you want the thread to begin)

Well, it's an implementation issue that start() must basically call run() in the new thread.  What I decided to do in Tango to lessen the confusion is to always have the user pass the method to run to the superclass if inheriting from Thread:

    class MyClass : Thread
    {
        this()
        {
            super( &run );
        }

        void run()
        {
            ...
        }
    }

> * wait can't be used inside a thread to wait for itself

The purpose of this method is to wait for the thread to complete, so if a thread could wait on itself the thread would effectively deadlock.  In Tango this method is called join(), which is more consistent with popular terminology.

> * yield has no specific timing constraints

It shouldn't.  yield() is simply intended to yield the current thread's timeslice to another thread.  On Win32, this is done via Sleep(0) or Sleep(1) (depending on how nice you want to be), and on Posix this is pthread_yield().

> * derr is not synchronized.  Debug output from different threads can get multiplexed together.

In my opinion, this is the correct choice from a performance standpoint.

> * msleep isn't available on all platforms (problem inherited from C)
> * usleep isn't thread safe (problem inherited from C)

This is only an issue because Phobos threads lack a sleep() routine. Tango threads do not.  And to reply to Regan as well, nanosleep isn't available everywhere either :-)  The most commonly implemented methods are sleep() and usleep() in <unistd.h>.  In fact, I think these may actually be required (nanosleep is a part of the realtime extensions IIRC).

> My big issue at the moment is how to cause a thread to delay (approximately) for a period of time that I specify.  I guess I might be ok with yield when the application is in full swing, but I really hate for nearly idle threads to peg the CPU when there's nothing to do.

You want sleep(), usleep(), or nanosleep() for Posix, and Sleep() or SleepEx() for Win32.


Sean
June 01, 2007
Sean Kelly Wrote:
> This is only an issue because Phobos threads lack a sleep() routine. Tango threads do not.  And to reply to Regan as well, nanosleep isn't available everywhere either :-)  The most commonly implemented methods are sleep() and usleep() in <unistd.h>.  In fact, I think these may actually be required (nanosleep is a part of the realtime extensions IIRC).

Oops, I didn't mean to imply nanosleep was available everywhere, I was trying to suggest what you did;  to use sleep(), usleep(), or nanosleep() for Posix, and Sleep() or SleepEx() for Win32.  ;)

Regan Heath


June 02, 2007
Sean Kelly wrote:
> Jason House wrote:
>> * derr is not synchronized.  Debug output from different threads can get multiplexed together.
> 
> In my opinion, this is the correct choice from a performance standpoint.

I may not appreciate the full impact on performance, but I can pretty much guarantee that jumbling the output of two different threads together is rarely what the user wants to see...


> 
>> * msleep isn't available on all platforms (problem inherited from C)
>> * usleep isn't thread safe (problem inherited from C)
> 
> This is only an issue because Phobos threads lack a sleep() routine. Tango threads do not.  And to reply to Regan as well, nanosleep isn't available everywhere either :-)  The most commonly implemented methods are sleep() and usleep() in <unistd.h>.  In fact, I think these may actually be required (nanosleep is a part of the realtime extensions IIRC).
> 
>> My big issue at the moment is how to cause a thread to delay (approximately) for a period of time that I specify.  I guess I might be ok with yield when the application is in full swing, but I really hate for nearly idle threads to peg the CPU when there's nothing to do.
> 
> You want sleep(), usleep(), or nanosleep() for Posix, and Sleep() or SleepEx() for Win32.

It's annoying to have to worry about reentrant guarantees in addition to which are available on which platform.  I notice tango's thread library has a sleep method that (theoretically) would solve the problem.

I should add to my list that I don't see any mutexes in phobos.
June 02, 2007
Jason House wrote:
> Sean Kelly wrote:
>> Jason House wrote:
>>> * derr is not synchronized.  Debug output from different threads can get multiplexed together.
>> 
>> In my opinion, this is the correct choice from a performance standpoint.
> 
> I may not appreciate the full impact on performance, but I can pretty much guarantee that jumbling the output of two different threads together is rarely what the user wants to see...
> 

I don't think you need more than this to fix that problem:

void debugOutput(char[] msg)
{
    synchronized(derr) {
        derr.writeLine(msg);
        derr.flush();  // might be a good idea  in case of redirection
    }
}
June 02, 2007
torhu wrote:
> I don't think you need more than this to fix that problem:
> 
> void debugOutput(char[] msg)
> {
>     synchronized(derr) {
>         derr.writeLine(msg);
>         derr.flush();  // might be a good idea  in case of redirection
>     }
> }

Of course, that loses a lot of the functionality of derr.writefln, but something more advanced could be done...

Sadly, that really isn't my big problem right now... I need to figure out how to sleep without crashing!

Program received signal SIGUSR1, User defined signal 1.
[Switching to Thread 1107310912 (LWP 30890)]
0x00002abf6306d881 in ?? () from /lib/libpthread.so.0
(gdb) up
#1  0x000000000044bcaf in _D6search19pureMonteCarloSlave3runMFZi (
    this=@0x2abf62c5f400) at search.d:131
131                                     nanosleep(&tv, null);
Current language:  auto; currently minimal
June 02, 2007
Hehe, this is no crash.
This is the garbage collector stopping all threads.
You need to configure GDB to ignore SIGUSR1 and SIGUSR2.

in GDB type
handle SIGUSR1 nostop noprint
handle SIGUSR2 nostop noprint

or make a startup skript or something like that.


Jason House schrieb:
> torhu wrote:
>> I don't think you need more than this to fix that problem:
>>
>> void debugOutput(char[] msg)
>> {
>>     synchronized(derr) {
>>         derr.writeLine(msg);
>>         derr.flush();  // might be a good idea  in case of redirection
>>     }
>> }
> 
> Of course, that loses a lot of the functionality of derr.writefln, but something more advanced could be done...
> 
> Sadly, that really isn't my big problem right now... I need to figure out how to sleep without crashing!
> 
> Program received signal SIGUSR1, User defined signal 1.
> [Switching to Thread 1107310912 (LWP 30890)]
> 0x00002abf6306d881 in ?? () from /lib/libpthread.so.0
> (gdb) up
> #1  0x000000000044bcaf in _D6search19pureMonteCarloSlave3runMFZi (
>     this=@0x2abf62c5f400) at search.d:131
> 131                                     nanosleep(&tv, null);
> Current language:  auto; currently minimal
June 02, 2007
Jason House wrote:
> torhu wrote:
>> I don't think you need more than this to fix that problem:
>> 
>> void debugOutput(char[] msg)
>> {
>>     synchronized(derr) {
>>         derr.writeLine(msg);
>>         derr.flush();  // might be a good idea  in case of redirection
>>     }
>> }
> 
> Of course, that loses a lot of the functionality of derr.writefln, but something more advanced could be done...
> 

You probably know this, but since I've written it out already...

void debugOutput(...)
{
    synchronized(derr) {
        derr.writefx(_arguments, _argptr, true);
        derr.flush();
    }
}

Sorry I can't help you with the other problem, haven't run into that.