std.concurrency.send
Nathan M. Swan
nathanmswan at gmail.com
Sun May 20 15:07:18 PDT 2012
On Sunday, 20 May 2012 at 04:09:50 UTC, japplegame wrote:
>> public:
>> void startLogger(LogConstructorArgs args) {
>> loggerTid = spawn(&loggerThread, args);
>> }
>>
>> void log(string msg, OtherOptions oo) {
>> loggerTid.send(LogMsg(msg, oo));
>> }
>>
>> void stopLogger() {
>> loggerTid.send(QuitMsg());
>> }
>>
>> private:
>> Tid loggerTid;
>>
>> struct LogMsg {
>> string msg;
>> OtherOptions oo;
>> }
>>
>> struct QuitMsg {}
>>
>> void loggerThread(LogConstructorArgs args) {
>> Logger lg = new Logger(args);
>> bool cont = true;
>> while(cont) {
>> receive((LogMsg lm) { lg.log(lm.msg, lm.oo); },
>> (QuitMsg qm) { cont = false; });
>> }
>> }
> I don't understand. In this way I should call
> startLogger/stopLogger in every application thread because
> loggerTid is thread related and every application thread has
> its own instanse of loggerTid. Instead N+1 threads (N
> application and 1 logger) we will get 2*N threads (N
> application and N loggers). Also we should synchronize Logger
> class because many instances of them will run concurrently.
> This is terrible.
Tids aren't threads, they're thread-IDs, so you wouldn't be
creating threads.
Though you're right in that tids would need to be shared (for
some reason I assumed that child thread TLS storage was copied
from its parents). In general, this shows an important principle
of D-ish concurrent programming: avoid globals.
In your situation, here's what I'd do:
struct Logger {
void start() { loggerTid = spawn(&loggerThread, args); }
void stop() { loggerTid.send(QuitMsg()); }
void log(string msg) { loggerTid.send(LogMsg(msg)); }
Tid loggerTid;
}
Construct loggerThread like the class-to-thread I show below, so
you don't need a Logger class, and make sure to start() at the
beginning and stop() at the end, and pass your Logger struct
around.
This may seem like an inconvenience compared to a shared
synchronized object, but here's an interesting thought: once a
working thread sends a message to the logger thread to log, it
can continue with its work. This makes it more efficient than
having to wait for the logging to finish before continuing with
work, which is what you'd do with synchronized(this). You are
only doing synchronized(queue) in the background.
What D is doing is making it easier to do it the right/efficient
way (message passing), by making the usually simple way (shared
objects) inconvenient.
Note: unfortunately, I'm not sure if the Logger will work due to
this bug:
http://d.puremagic.com/issues/show_bug.cgi?id=4957
Hopefully it's fixed soon :(
>> If you are passing objects between threads, make it shared.
>> This might seem annoying, but in general you should try to
>> shift your thinking into having thread-local objects and
>> communicating via structs.
>>
>> But when you use global/singleton objects (any case where
>> there's one instance of the class), convert it into a thread,
>> FROM
>>
>> class <name> {
>> this(<cons args>) {
>> <constructor>
>> }
>>
>> void <method1>() {
>> // ...
>> }
>>
>> int <method2>() {
>> int result;
>> // ...
>> return result;
>> }
>>
>> <private fields> // you encapsulate and have no public
>> fields, right?
>> }
>>
>> TO
>>
>> void <name>Thread(<cons args>) {
>> <private fields>
>> <constructor>
>> bool cont = true;
>> while(cont) {
>> receive(
>> (<method1>Msg) {
>> // ...
>> },
>> (Tid r, <method2>Msg) {
>> int result;
>> // ...
>> r.send(<method2>ReturnMsg(result));
>> }
>> (QuitMsg qm) {cont = false;});
>> }
>> }
> Intresting idea. I will try it. Thanks.
I like to thing of this idea as the "thread-object" paradigm.
I've considered using D's great templating ability to allow you
to make a class and have it be converted to a thread-object, but
the __traits(getMember) stuff is confusing.
NMS
More information about the Digitalmars-d-learn
mailing list