www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Using objects that manage threads via std.concurrency

reply "monarch_dodra" <monarchdodra gmail.com> writes:
I've been trying out std.concurrency, and it's MPI, and found it 
very easy to use. It has been working well in most of my toy 
programs so far, but I see a HUGE glaring flaw with it. I mean: 
SHOWSTOPPER.

Basically, I can't help but feel the thing has an hopelessly 
thread-global "mailbox" approach to the problem. This is all fine 
and dandy if there is only a single "canal" of communication 
between the master and the child/children.

But what happens if you have 2 objects at once that want to 
communicate with their children? They have to share the global 
mailbox, making things very complex.

In my program, I have simple objects: "Manager"s, that spawn a 
child thread to do work. This works fine if I have a single 
Manager, but how do I manage having 2 Managers at once? What 
should a manager do if it calls "receive", and notices the 
message wasn't meant for him?

I can write an internal service that can manage the mailbox for 
all my managers, but that is a *very* closed approach. In 
particular, I'm planning to distribute a module with integrated 
multithreading. What if my clients also want to do MPI on their 
end? How does that work?

------------

Is there any way for an object to create its own personal 
MailBox? How would you tackle the problem? Does any one have any 
(simple) literature on the subject?
Feb 11 2013
next sibling parent reply FG <home fgda.pl> writes:
On 2013-02-11 22:37, monarch_dodra wrote:
 Basically, I can't help but feel the thing has an hopelessly thread-global
 "mailbox" approach to the problem. This is all fine and dandy if there is only
a
 single "canal" of communication between the master and the child/children.
What thread-global? Every mbox is in thread-local storage.
 But what happens if you have 2 objects at once that want to communicate with
 their children? They have to share the global mailbox, making things very
complex.
Caller locks the callee mailbox for a moment to put a message. Doesn't lock any other thread, so you can have N/2 threads writing to other N/2 at the same time.
 In my program, I have simple objects: "Manager"s, that spawn a child thread to
 do work. This works fine if I have a single Manager, but how do I manage having
 2 Managers at once?
It's generally a very bad idea to have more than 1 manager over one's head. :)
 What should a manager do if it calls "receive", and notices the message wasn't
 meant for him?
Don't know. Kill the messenger perhaps? :)
Feb 11 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 12 February 2013 at 00:09:40 UTC, FG wrote:
 On 2013-02-11 22:37, monarch_dodra wrote:
 Basically, I can't help but feel the thing has an hopelessly 
 thread-global
 "mailbox" approach to the problem. This is all fine and dandy 
 if there is only a
 single "canal" of communication between the master and the 
 child/children.
What thread-global? Every mbox is in thread-local storage.
Yes, but there is only one global mailbox per thread. If I have more than one class instance inside a thread trying to communicate with other threads, they have to share the box.
 But what happens if you have 2 objects at once that want to 
 communicate with
 their children? They have to share the global mailbox, making 
 things very complex.
Caller locks the callee mailbox for a moment to put a message. Doesn't lock any other thread, so you can have N/2 threads writing to other N/2 at the same time.
I think there is a misunderstanding about what I meant by "global mailbox".
 In my program, I have simple objects: "Manager"s, that spawn a 
 child thread to
 do work. This works fine if I have a single Manager, but how 
 do I manage having
 2 Managers at once?
It's generally a very bad idea to have more than 1 manager over one's head. :)
 What should a manager do if it calls "receive", and notices 
 the message wasn't
 meant for him?
Don't know. Kill the messenger perhaps? :)
Feb 11 2013
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 02/11/2013 01:37 PM, monarch_dodra wrote:

 What should a manager do if it
 calls "receive", and notices the message wasn't meant for him?
Threads receive their own messages. If there is a specific receiver of a message, then the child sends it to that receiver. As FG said, every thread has a separate mailbox. It is possible to introduce threads to each other by their thread ids, which can be mapped to arbitrary names. (See register, locate, and unregister in std.concurrency.)
 Does any one have any (simple) literature on the subject?
My experiments have been documented in the following chapter: http://ddili.org/ders/d.en/concurrency.html The "Thread names" section in there has a simple example that involves a third party introducing two threads to each other. Ali
Feb 11 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 12 February 2013 at 06:29:22 UTC, Ali Çehreli wrote:
 On 02/11/2013 01:37 PM, monarch_dodra wrote:

 What should a manager do if it
 calls "receive", and notices the message wasn't meant for him?
Threads receive their own messages. If there is a specific receiver of a message, then the child sends it to that receiver. As FG said, every thread has a separate mailbox.
I think I didn't explain myself very well. I have my single "master" thread which has a "thread-global" mailbox, but I have 3 different objects that are sharing that mailbox. Code example: //---- import std.stdio, std.concurrency; struct Manager { this(string s) { spawn(&worker, s, thisTid); } string get() { return receiveOnly!string(); } } void worker(string s, Tid owner) { owner.send(s); } void main() { auto ma = Manager("a"); auto mb = Manager("b"); auto mc = Manager("c"); writeln(ma.get()); writeln(mb.get()); writeln(mb.get()); } //---- How can I get my 3 managers to co-exist, when they are all sharing the same box? How can I make sure the workers are sending their messages to the correct manager?
 It is possible to introduce threads to each other by their 
 thread ids, which can be mapped to arbitrary names. (See 
 register, locate, and unregister in std.concurrency.)
Yes, but in this case, the problem is not thread to thread communication, but rather thread to object
 Does any one have any (simple) literature on the subject?
My experiments have been documented in the following chapter: http://ddili.org/ders/d.en/concurrency.html The "Thread names" section in there has a simple example that involves a third party introducing two threads to each other. Ali
I have already fully read that tutorial, which was very helpful. Thank you.
Feb 11 2013
next sibling parent reply Jonathan M Davis <jmdavisProg gmx.com> writes:
On Tuesday, February 12, 2013 07:58:04 monarch_dodra wrote:
 How can I get my 3 managers to co-exist, when they are all
 sharing the same box? How can I make sure the workers are sending
 their messages to the correct manager?
By making it so that their receiving functions take unique types. If need be, you can declare types specific to the channel of communication that you're trying to create. Then only functions which match will receive those messages.
 It is possible to introduce threads to each other by their
 thread ids, which can be mapped to arbitrary names. (See
 register, locate, and unregister in std.concurrency.)
Yes, but in this case, the problem is not thread to thread communication, but rather thread to object
Which I don't think was ever really intended. That doesn't mean that it's unreasonable, but I think that it was always the idea that a particular thread had a particular job, in which case, you wouldn't generally be trying to send messages to different parts of the thread. - Jonathan M Davis
Feb 11 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 12 February 2013 at 07:07:21 UTC, Jonathan M Davis 
wrote:
 Which I don't think was ever really intended. That doesn't mean 
 that it's
 unreasonable, but I think that it was always the idea that a 
 particular thread
 had a particular job, in which case, you wouldn't generally be 
 trying to send
 messages to different parts of the thread.

 - Jonathan M Davis
Hum, I just realized that "receive" works out of order on the types requested. I thought it *had* to receive THE first message in the queue, and throw if the type is not supported. I guess then that by specifying my specific type, and having a dedicated dispatcher, I can make my program work, without clashing with anybody else who is also threading. Now, I've just got to figure out how to manage my master's mailbox sizes, if a worker is faster than the rest.
Feb 12 2013
prev sibling parent reply FG <home fgda.pl> writes:
On 2013-02-12 07:58, monarch_dodra wrote:
 I think I didn't explain myself very well. I have my single "master" thread
 which has a "thread-global" mailbox, but I have 3 different objects that are
 sharing that mailbox.
OK, I finally get what you are saying. You need to create a mailbox and a unique tid for every Manager (and probably would have to change Manager into a class). Unfortunately this won't work out of the box, as for example receiveOnly and friends use only the default mailbox of the current thread. struct Manager { Tid tid; MessageBox mbox; this(string s) { this.mbox = new MessageBox tid = Tid(new Mailbox); spawn(&worker, s, tid); } string get() { // you'd have to rewrite receive to use custom mbox return tid.myReceiveOnly!string(); } }
Feb 12 2013
parent reply "monarch_dodra" <monarchdodra gmail.com> writes:
On Tuesday, 12 February 2013 at 10:08:14 UTC, FG wrote:
 On 2013-02-12 07:58, monarch_dodra wrote:
 I think I didn't explain myself very well. I have my single 
 "master" thread
 which has a "thread-global" mailbox, but I have 3 different 
 objects that are
 sharing that mailbox.
OK, I finally get what you are saying. You need to create a mailbox and a unique tid for every Manager (and probably would have to change Manager into a class). Unfortunately this won't work out of the box, as for example receiveOnly and friends use only the default mailbox of the current thread. struct Manager { Tid tid; MessageBox mbox; this(string s) { this.mbox = new MessageBox tid = Tid(new Mailbox); spawn(&worker, s, tid); } string get() { // you'd have to rewrite receive to use custom mbox return tid.myReceiveOnly!string(); } }
Hum, I'll have to try to play around with that. For one thing, "MessageBox" is private. Good news is my manager is already a class. As for the re-implement of receive to work on a custom Tid, maybe it might be better to forget about the tid, and implement it on directly on the mailbox? Something like this: //---- struct Manager { MessageBox mbox; this(string s) { this.mbox = new MessageBox Tid managerTid = Tid(new Mailbox); spawn(&worker, s, managerTid); } string get() { // you'd have to rewrite receive to use custom mbox return mbox.receiveOnly!string(); //Or just straight up: mbox.get(); } } //---- I don't know, I'll try and see how it goes.
Feb 12 2013
parent reply FG <home fgda.pl> writes:
On 2013-02-12 12:14, monarch_dodra wrote:
 For one thing, "MessageBox" is private.
Unnecessarily hidden, because, from what I can see from a fast look at the sources, there is no implicit requirement for there to be only one MessageBox per thread. Maybe we're getting somewhere and this will be changed.
 As for the re-implement of receive to work on a custom Tid, maybe it might be
 better to forget about the tid, and implement it on directly on the mailbox?
Well, yes. It's more natural to work on mbox than some artificial struct. Now, as for the usefulness of having many mailboxes. I'd rather have one mailbox than go into a loop with receiveTimeout called for each Manager, but in your divide&conquer example receive makes sense and keeps ordering.
Feb 12 2013
parent "monarch_dodra" <monarchdodra gmail.com> writes:
THREAD HAS BEEN MOVED TO:

http://forum.dlang.org/thread/hatfmxuccjwraqwrzshq forum.dlang.org
Feb 12 2013