September 27, 2020
On Sunday, 27 September 2020 at 10:40:25 UTC, Ali Çehreli wrote:
> On 9/27/20 3:06 AM, Ferhat Kurtulmuş wrote:

> Have you considered passing messages with std.concurrency.send() and std.concurrency.receive() and friends? You wouldn't need 'queue' because all of your threads already have mail boxes to send messages to each other.

I remember that your book covers passing messages with send(). Probably I will rewrite it using that mechanism, you are right, I noticed that when I run the code I can hear the boosted noise of my desktop fan.

> As an improvement when defining durations, you don't need to "hide" units in comments:
>
>         // enum afterNmilliseconds = 1500;
>
>         // Instead:
>         enum after = 1500.msecs;
>
> msecs and friends are defined in core.time:
>
>   https://dlang.org/phobos/core_time.html#.dur

Thank you for the tip. That was just a preudo-code to explain my situation.

Thanks a lot.
Ferhat


September 27, 2020
On Sunday, 27 September 2020 at 10:52:58 UTC, Ferhat Kurtulmuş wrote:
> On Sunday, 27 September 2020 at 10:40:25 UTC, Ali Çehreli wrote:
>> [...]
>
>> [...]
>
> I remember that your book covers passing messages with send(). Probably I will rewrite it using that mechanism, you are right, I noticed that when I run the code I can hear the boosted noise of my desktop fan.
>
>>   [...]
>
> Thank you for the tip. That was just a preudo-code to explain my situation.
>
> Thanks a lot.
> Ferhat

The actor model is under-rated imho
September 27, 2020
On Sunday, 27 September 2020 at 10:40:25 UTC, Ali Çehreli wrote:
> On 9/27/20 3:06 AM, Ferhat Kurtulmuş wrote:
>
> > __gshared DList!Entry queue;
> > __gshared bool shouldRun = true;
>
> Have you considered passing messages with std.concurrency.send() and std.concurrency.receive() and friends? You wouldn't need 'queue' because all of your threads already have mail boxes to send messages to each other.
>
> > void worker() {
> >      while(shouldRun){
> >          auto r = queue[];
> >          if(!r.empty && queue.back.st < Clock.currTime){
> >              writeln(queue.back); // consume the value
> > sendPWMSignalToValfe(pwmval)
> >              queue.popLastOf(r);
> >          }
> >      }
> > }
>
> It's not clear whether it's only in your test code but busy-waiting like that will make your CPU very warm. :) Since requests cannot pass each other, your worker thread should have something like the following in that loop:
>
> import core.thread;
>
>   Thread.sleep(duration);
>
> Depending on how accurate the operating system honors your sleep requests (e.g. is it real-time?), you may want to sleep less than 'duration' and then busy-wait the rest of the duration. Similar to the difference between spinForce() and yieldForce() of std.parallelism (I understand that your solution should not involve std.parallelism):
>
>   https://dlang.org/phobos/std_parallelism.html#.Task.spinForce
>
> As an improvement when defining durations, you don't need to "hide" units in comments:
>
>         // enum afterNmilliseconds = 1500;
>
>         // Instead:
>         enum after = 1500.msecs;
>
> msecs and friends are defined in core.time:
>
>   https://dlang.org/phobos/core_time.html#.dur
>
> Ali

Yes, this solution requires less code and obviously less system resources.

void main() {

    while (true) {
        int v;

        "type your value: ".write;
        readf(" %d", &v);

        if(v==0){
            break;
        }

        auto childTid = spawn(&spawnedFunc, thisTid);
        send(childTid, v);
    }

    writeln("main is done.");
}

static void spawnedFunc(Tid ownerTid)
{
    receive((int v){
        Thread.sleep(1500.msecs);
        writeln(v);
    });
}

However, there is a big problem now. If I change my main like below, numbers are not written at the correct order after 1.5 seconds?

void main() {
    foreach (v; 0..10){
        auto childTid = spawn(&spawnedFunc, thisTid);
        send(childTid, v);
    }
    writeln("main is done.");
}

September 27, 2020
On Sunday, 27 September 2020 at 12:05:13 UTC, Ferhat Kurtulmuş wrote:
> On Sunday, 27 September 2020 at 10:40:25 UTC, Ali Çehreli wrote:
>> On 9/27/20 3:06 AM, Ferhat Kurtulmuş wrote:

Oh, It will work fine if I imitate my time-consuming image processing like this.
I think it is Ok now.

import std.stdio;
import std.concurrency;
import core.thread;

void main() {
    foreach (v; 0..10){
        auto childTid = spawn(&spawnedFunc, thisTid);
        Thread.sleep(10.msecs); // imitate image processing
        send(childTid, v);

    }
    writeln("main is done.");
}

static void spawnedFunc(Tid ownerTid)
{
    receive((int v){
        Thread.sleep(1500.msecs);
        writeln(v);
    });
}
September 27, 2020
On 9/27/20 6:33 AM, Ferhat Kurtulmuş wrote:

> On Sunday, 27 September 2020 at 12:05:13 UTC, Ferhat Kurtulmuş wrote:
>> On Sunday, 27 September 2020 at 10:40:25 UTC, Ali Çehreli wrote:
>>> On 9/27/20 3:06 AM, Ferhat Kurtulmuş wrote:
>
> Oh, It will work fine if I imitate my time-consuming image processing
> like this.
> I think it is Ok now.
>
> import std.stdio;
> import std.concurrency;
> import core.thread;
>
> void main() {
>      foreach (v; 0..10){
>          auto childTid = spawn(&spawnedFunc, thisTid);

How many flame threads do you need? I thought one image processor and one flame thrower, no? Even if you have a dozen of each, main can start only the image processing threads and then each image processor can start its own flame thrower. Then, each pair will have an owner and a worker.

You don't need to send thisTid because every thread already has an ownerTid defined:

  auto childTid = spawn(&spawnedFunc);

>          Thread.sleep(10.msecs); // imitate image processing
>          send(childTid, v);

UFCS makes it nicer:

  childTid.send(v);

>
>      }
>      writeln("main is done.");
> }
>
> static void spawnedFunc(Tid ownerTid)

To repeat, you already have a valid ownerTid in this thread. Just remove the parameter.

> {
>      receive((int v){
>          Thread.sleep(1500.msecs);

I think you should sleep less than that to work at the exact expected time. Otherwise, an unknown amount of time has already passed when this thread is woken up again.

Instead of sleeping 1500, something like this may be needed:

  - This thread looks at the time to figure out how long to sleep e.g. sometimes 1400 msecs
  - Sleeps that amount
  - Fires when it wakes up

However, you can not expect to be waken up exactly at 1400 msecs later. If timing precision is really important, I recommend running some statistics to see how much off your thread is when it wakes up. Depending on the statistics, I would sleep less than the expected amount and then burn the CPU until it's the exact time. But maybe precision is not that important; so, forget that idea. :)

>          writeln(v);
>      });
> }

One more thing: It is common for the workers to die with an exception (sometimes with Error). You must catch it (including Error) by the worker thread and report it somehow e.g. with a special exception. Otherwise, nobody will know what happened.

This reminds me: If you start the worker with spawnLinked() instead of spawn(), the owner will get a LinkTerminated message if a thread dies. That's another way of detecting that failure.

Ali


September 27, 2020
On Sunday, 27 September 2020 at 16:39:45 UTC, Ali Çehreli wrote:
> On 9/27/20 6:33 AM, Ferhat Kurtulmuş wrote:
>
> > [...]
> Kurtulmuş wrote:
> >> [...]
> wrote:
> > [...]
> processing
> >          [...]
>
> How many flame threads do you need? I thought one image processor and one flame thrower, no? Even if you have a dozen of each, main can start only the image processing threads and then each image processor can start its own flame thrower. Then, each pair will have an owner and a worker.
>
> [...]

Thank you Ali (Bey). I will take those into account when finalizing my code. Your comments helped a lot!
1 2
Next ›   Last »