www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Proper way to code multithreaded applications?

reply Jason House <jason.james.house gmail.com> writes:
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.
May 31 2007
next sibling parent Regan Heath <regan netmail.co.nz> writes:
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
Jun 01 2007
prev sibling parent reply Sean Kelly <sean f4.ca> writes:
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
Jun 01 2007
next sibling parent Regan Heath <regan netmail.co.nz> writes:
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
Jun 01 2007
prev sibling parent reply Jason House <jason.james.house gmail.com> writes:
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.
Jun 01 2007
parent reply torhu <fake address.dude> writes:
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 } }
Jun 02 2007
parent reply Jason House <jason.james.house gmail.com> writes:
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
Jun 02 2007
next sibling parent Frank Benoit <keinfarbton googlemail.com> writes:
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

Jun 02 2007
prev sibling parent torhu <fake address.dude> writes:
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.
Jun 02 2007