www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Obedient threads

reply "Chris" <wendlec tcd.ie> writes:
What is the best way to kill a thread when it pleases the owner 
(main thread)? The background is playing audio files. The 
playback happens in a separate thread so the owner can keep on 
listening to events triggered by the user (like stop, pause). I 
have to queue audio files, wait until one has finished, then play 
the next etc. Communicating between threads does not work with 
this technique:

Owner:
// create thread ...
...
auto isFinished = receiveOnly!bool();

Thread:
// play file ...
if (finished) {
   ownerTid.send(true);
}

Of course, now Owner waits until it receives the message and is 
deaf to any user input until Thread has finished, thus there is 
no way to interrupt the playback. Is there a simple and elegant D 
way to solve this? Slots are thread local so the observer doesn't 
know what's going on in another thread, does it?
Oct 02 2014
parent reply "thedeemon" <dlang thedeemon.com> writes:
Just use non-blocking receives in main thread's event loop. When 
you get a message from child thread that it's finished playing 
and you decide you don't need that thread anymore, send a message 
to child "you're dismissed". The child should also have some loop 
to check for incoming messages non-blockingly. Upon receiving 
such message it should exit.
Oct 02 2014
parent reply "Chris" <wendlec tcd.ie> writes:
On Thursday, 2 October 2014 at 10:33:02 UTC, thedeemon wrote:
 Just use non-blocking receives in main thread's event loop. 
 When you get a message from child thread that it's finished 
 playing and you decide you don't need that thread anymore, send 
 a message to child "you're dismissed". The child should also 
 have some loop to check for incoming messages non-blockingly. 
 Upon receiving such message it should exit.
Thanks. I was thinking of something like that, only I haven't found a way to set up non-blocking receives. What am I missing. I'm sure it's something trivial.
Oct 02 2014
parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Thu, 02 Oct 2014 11:36:06 +0000
Chris via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 Thanks. I was thinking of something like that, only I haven't=20
 found a way to set up non-blocking receives. What am I missing.=20
 I'm sure it's something trivial.
you can use receiveTimeout! to check if there is some message available.
Oct 02 2014
parent reply "Chris" <wendlec tcd.ie> writes:
On Thursday, 2 October 2014 at 13:05:00 UTC, ketmar via 
Digitalmars-d-learn wrote:
 On Thu, 02 Oct 2014 11:36:06 +0000
 Chris via Digitalmars-d-learn 
 <digitalmars-d-learn puremagic.com> wrote:

 you can use receiveTimeout! to check if there is some message 
 available.
That won't do. It blocks the main thread too (for the duration of timeout), and it might abandon the thread too early. If you do it like in Ali's example[1], the main thread is blocked in the sense that it does not listen to input. [1] http://ddili.org/ders/d.en/concurrency.html
Oct 02 2014
next sibling parent ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Thu, 02 Oct 2014 13:49:33 +0000
Chris via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 That won't do. It blocks the main thread too (for the duration of=20
 timeout)
don't do hour-long timeouts. 1ms timeout is actually "poll-and-receive". can't see any problems with it.
Oct 02 2014
prev sibling parent reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
On 10/02/2014 06:49 AM, Chris wrote:
 On Thursday, 2 October 2014 at 13:05:00 UTC, ketmar via
 Digitalmars-d-learn wrote:
 On Thu, 02 Oct 2014 11:36:06 +0000
 Chris via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 you can use receiveTimeout! to check if there is some message available.
That won't do. It blocks the main thread too (for the duration of timeout), and it might abandon the thread too early. If you do it like in Ali's example[1], the main thread is blocked in the sense that it does not listen to input. [1] http://ddili.org/ders/d.en/concurrency.html
To add to what ketmar said, even 0.msecs works. In such a case moving the other tasks out of the main thread may be a better option. main can safely block on the message queue while another thread interacts with the outside world. It would be a cleaner event loop that way. I've just improved that example to interact with the user. The worker produces a random message periodically. The user can ask for the most recent message by entering "yes". (I made it so that reading the message also clears it.) import std.stdio; import std.concurrency; import core.thread; import std.string; import std.random; import std.array; void workerFunc(size_t count, Duration duration) { writefln("There will be %s messages every %s.", count, duration); foreach (i; 0 .. count) { Thread.sleep(duration); ownerTid.send(format("message %s: %s", i, uniform(0, 100))); } writeln("workerFunc exiting"); } struct ResultPlease {} void interactor() { bool done = false; while (!done) { write("Would you like to see the result? "); string response = readln.chomp; if (response == "yes") { ownerTid.send(ResultPlease(), thisTid); const result = receiveOnly!string(); if (result.empty) { writeln("Sorry, no result yet."); } else { writefln(`The result is "%s"`, result); } } else { writeln("Ok, no more interaction. Bye."); done = true; } } } void main() { spawnLinked(&workerFunc, 4, 5.seconds); spawnLinked(&interactor); string result; Tid[] completed; while (completed.length < 2) { receive( (string message) { result = message; }, (ResultPlease request, Tid requestor) { requestor.send(result); result = ""; }, (LinkTerminated e) { completed ~= e.tid; } ); } } Ali
Oct 02 2014
parent reply "Chris" <wendlec tcd.ie> writes:
On Thursday, 2 October 2014 at 18:08:40 UTC, Ali Çehreli wrote:
 On 10/02/2014 06:49 AM, Chris wrote:
 On Thursday, 2 October 2014 at 13:05:00 UTC, ketmar via
 Digitalmars-d-learn wrote:
 On Thu, 02 Oct 2014 11:36:06 +0000
 Chris via Digitalmars-d-learn 
 <digitalmars-d-learn puremagic.com> wrote:

 you can use receiveTimeout! to check if there is some message 
 available.
That won't do. It blocks the main thread too (for the duration of timeout), and it might abandon the thread too early. If you do it like in Ali's example[1], the main thread is blocked in the sense that it does not listen to input. [1] http://ddili.org/ders/d.en/concurrency.html
To add to what ketmar said, even 0.msecs works. In such a case moving the other tasks out of the main thread may be a better option. main can safely block on the message queue while another thread interacts with the outside world. It would be a cleaner event loop that way. I've just improved that example to interact with the user. The worker produces a random message periodically. The user can ask for the most recent message by entering "yes". (I made it so that reading the message also clears it.) import std.stdio; import std.concurrency; import core.thread; import std.string; import std.random; import std.array; void workerFunc(size_t count, Duration duration) { writefln("There will be %s messages every %s.", count, duration); foreach (i; 0 .. count) { Thread.sleep(duration); ownerTid.send(format("message %s: %s", i, uniform(0, 100))); } writeln("workerFunc exiting"); } struct ResultPlease {} void interactor() { bool done = false; while (!done) { write("Would you like to see the result? "); string response = readln.chomp; if (response == "yes") { ownerTid.send(ResultPlease(), thisTid); const result = receiveOnly!string(); if (result.empty) { writeln("Sorry, no result yet."); } else { writefln(`The result is "%s"`, result); } } else { writeln("Ok, no more interaction. Bye."); done = true; } } } void main() { spawnLinked(&workerFunc, 4, 5.seconds); spawnLinked(&interactor); string result; Tid[] completed; while (completed.length < 2) { receive( (string message) { result = message; }, (ResultPlease request, Tid requestor) { requestor.send(result); result = ""; }, (LinkTerminated e) { completed ~= e.tid; } ); } } Ali
Thanks Ali, you're a legend! I was actually thinking of creating a separate thread as an event listener instead of using main. I'll try that now. Somehow the 1.msecs solution doesn't seem clean enough.
Oct 02 2014
parent reply ketmar via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> writes:
On Thu, 02 Oct 2014 20:42:49 +0000
Chris via Digitalmars-d-learn <digitalmars-d-learn puremagic.com> wrote:

 I'll try that now. Somehow the 1.msecs solution doesn't seem=20
 clean enough.
it seems that you want thread messaging to be integrated in your event loop. sorry, there is no easy way to do it now. maybe if libasync will become std.async we will have such feature in phobos out-of-the-box.
Oct 02 2014
parent "Chris" <wendlec tcd.ie> writes:
On Friday, 3 October 2014 at 04:39:38 UTC, ketmar via 
Digitalmars-d-learn wrote:
 On Thu, 02 Oct 2014 20:42:49 +0000
 Chris via Digitalmars-d-learn 
 <digitalmars-d-learn puremagic.com> wrote:

 I'll try that now. Somehow the 1.msecs solution doesn't seem 
 clean enough.
it seems that you want thread messaging to be integrated in your event loop. sorry, there is no easy way to do it now. maybe if libasync will become std.async we will have such feature in phobos out-of-the-box.
Exactly. But Ali's solution above might work for my program, if I can create a listener thread. The main thread will take care of queuing the audio files and the listener thread checks, if the user has chosen to interrupt the whole thing. The other way around of what I had first in mind.
Oct 03 2014