Thread overview
"shared" status
Apr 15, 2012
Kapps
Apr 16, 2012
Zardoz
Apr 17, 2012
Ali Çehreli
Apr 17, 2012
Luis
Apr 17, 2012
Ali Çehreli
Apr 17, 2012
Dejan Lekic
April 14, 2012
What is the status of "shared" types ?

I try it with gdmd v4.6.3
And I not get any warring/error when I do anything over a shared variable
without using atomicOp. It's normal ?

shared ushort ram[ram_size];
....
....
ram[i] = cast(ushort) (bytes[0] | bytes[1] << 8);

-- 
I'm afraid that I have a blog: http://zardoz.es
April 15, 2012
On Saturday, 14 April 2012 at 10:48:16 UTC, Luis Panadero Guardeño wrote:
> What is the status of "shared" types ?
>
> I try it with gdmd v4.6.3
> And I not get any warring/error when I do anything over a shared variable
> without using atomicOp. It's normal ?
>
> shared ushort ram[ram_size];
> ....
> ....
> ram[i] = cast(ushort) (bytes[0] | bytes[1] << 8);

Shared is at the moment (in my opinion anyways) not useable. Very little in Phobos is shared friendly. Most benefits of shared aren't implemented yet. I personally avoid it.
April 16, 2012
El Sun, 15 Apr 2012 23:05:55 +0200, Kapps escribió:

> On Saturday, 14 April 2012 at 10:48:16 UTC, Luis Panadero Guardeño wrote:
>> What is the status of "shared" types ?
>>
>> I try it with gdmd v4.6.3
>> And I not get any warring/error when I do anything over a shared
>> variable
>> without using atomicOp. It's normal ?
>>
>> shared ushort ram[ram_size];
>> ....
>> ....
>> ram[i] = cast(ushort) (bytes[0] | bytes[1] << 8);
> 
> Shared is at the moment (in my opinion anyways) not useable. Very little in Phobos is shared friendly. Most benefits of shared aren't implemented yet. I personally avoid it.

So, if I need to share a array of 0x10000 elements between 3 or more threads, how should do it ?
April 17, 2012
On 04/16/2012 03:57 AM, Zardoz wrote:

> So, if I need to share a array of 0x10000 elements between 3 or more
> threads, how should do it ?

1) The following program starts four threads to fill different parts of a shared array:

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

void numberFiller(shared(int)[] area, int fillValue)
{
    foreach (ref number; area) {
        number = fillValue;
    }
}

void main()
{
    enum totalNumbers = 0x10;
    auto numbers = new shared(int)[totalNumbers];

    enum totalThreads = 4;
    enum numbersPerThread = totalNumbers / totalThreads;

    foreach (i; 0 .. totalThreads) {
        immutable start = i * numbersPerThread;
        immutable fillValue = i;

        spawn(&numberFiller,
              numbers[start .. start + numbersPerThread],
              cast(int)fillValue);
    }

    thread_joinAll();

    writeln(numbers);
}

The output:

[0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3]


2) The program above is being careful to limit the threads to different parts of the array. In other cases lock-based multi-threading can be used. The following program allows four thread append to a single array as they get a hold of the slice:

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

class Job
{
    int[] * slice;
    size_t count;

    this(ref int[] slice, size_t count)
    {
        this.slice = &slice;
        this.count = count;
    }
}

void numberAppender(shared(Job) job, int appendValue)
{
    foreach (i; 0 .. job.count) {
        synchronized (job) {
            *job.slice ~= appendValue;
        }

        Thread.sleep(dur!"msecs"(uniform(1,100)));
    }
}

void main()
{
    enum totalNumbers = 0x10;
    int[] numbers;

    enum totalThreads = 4;
    enum numbersPerThread = totalNumbers / totalThreads;

    auto job = new shared(Job)(numbers, numbersPerThread);

    foreach (i; 0 .. totalThreads) {
        int appendValue = i;

        spawn(&numberAppender, job, appendValue);
    }

    thread_joinAll();

    writeln(numbers);
}

The output should be similar to this:

[0, 1, 3, 2, 1, 1, 2, 0, 3, 1, 3, 0, 2, 3, 0, 2]

(Note: I wish there were 'ref' variables in D. That's why Job.slice above had to be a pointer.)


3) Better than the two approaches above may be to use message passing and have the threads produce separate results to be either combined later or simply used separately:

import std.stdio;
import std.concurrency;

void arrayMaker(Tid owner, int count, int value)
{
    immutable(int)[] result;

    foreach (i; 0 .. count) {
        result ~= value;
    }

    owner.send(result);
}

void main()
{
    enum totalNumbers = 0x10;
    enum totalThreads = 4;
    enum numbersPerThread = totalNumbers / totalThreads;

    foreach (i; 0 .. totalThreads) {
        int value = i;
        spawn(&arrayMaker, thisTid, numbersPerThread, value);
    }

    immutable(int[])[] results;

    foreach (i; 0 .. totalThreads) {
        auto result = receiveOnly!(immutable(int)[])();
        results ~= result;
    }

    writeln(results);
}

The output should be similar to this:

[[0, 0, 0, 0], [1, 1, 1, 1], [3, 3, 3, 3], [2, 2, 2, 2]]

(Note: I could not pass the results as shared int slices so I went back to immutable.)

Ali

April 17, 2012
Thanks! It's very useful.

Ali Çehreli wrote:

> synchronized (job) {
> *job.slice ~= appendValue;
> }
So shared, at least share data across threads. And using synchronized( ) I could do lock-based access to shared data.

April 17, 2012
On 04/17/2012 06:05 AM, Luis wrote:
> Thanks! It's very useful.
>
> Ali Çehreli wrote:
>
>> synchronized (job) {
>> *job.slice ~= appendValue;
>> }
> So shared, at least share data across threads. And using synchronized( )
> I could do lock-based access to shared data.
>

Yes. I've used the same Job object there but any class is a lock. (This has been a new concept for me. The "lock part" of the object is called the monitor.)

Ali
April 17, 2012
On Saturday, 14 April 2012 at 10:48:16 UTC, Luis Panadero Guardeño wrote:
> What is the status of "shared" types ?
>
> I try it with gdmd v4.6.3
> And I not get any warring/error when I do anything over a shared variable
> without using atomicOp. It's normal ?
>
> shared ushort ram[ram_size];
> ....
> ....
> ram[i] = cast(ushort) (bytes[0] | bytes[1] << 8);

Shared is crucial for concurrency/parallelism since the switch to the thread local storage as default storage.

Immutable values are IMPLICITLY SHARED while for your mutable data you have to explicitly use "shared" keyword.

This basically means that SHARED data are used everywhere in D applications nowadays.