Shared an non-shared

Jonathan M Davis via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Wed Oct 5 05:48:07 PDT 2016


On Wednesday, October 05, 2016 11:25:57 Begah via Digitalmars-d-learn wrote:
> On Wednesday, 5 October 2016 at 07:36:58 UTC, Jonathan M Davis
>
> wrote:
> > On Tuesday, October 04, 2016 19:22:10 Begah via
> >
> > Digitalmars-d-learn wrote:
> >> How can I make a method that accepts being called by both a
> >> shared and non-shared object, to prevent having to copy
> >> methods and adding a "shared"?
> >
> > You could templatize them, but really, the idea is that you
> > _don't_ call much of anything on a shared object. It's not
> > thread-safe to do so unless it's protected by a mutex or
> > sychronized block. shared is as unwieldy as it is in part
> > because it's easy to use incorrectly, and most code should not
> > be using shared at all, because the stuff that actually needs
> > to be shared across threads is normally pretty minimal.
>
> Thanks for the reply,
>
> One of my problem is that, i need all of my data to be accessible
> by both threads ( I have two ). I am making a 3d application and
> decided to separate the update loop and the render loop ( I just
> created another thread for the update loop meanwhile the render
> loop has to remain on the main thread ).
>
> I just want to ensure that when my render loop ( or update loop )
> updates/render an object, the other loop cannot ( To avoid
> potential bugs whereas one loop changes the position component
> and the render loop renders the object with only the new x
> position because the y and z variable haven't been changed yet ).
>
> As i will have many of those objects, do i need to create a mutex
> for everyone of them?

Unless you're writing lock-free algorithms (which really should only be done
by experts, and even then, they should probably reconsider it, since they're
so insanely hard to get right), _every_ variable/object that's going to be
accessible from multiple threads needs to be protected by a mutex so that
it's guaranteed that only one thread accesses the object at a time. That
would be just as true in C/C++ as it is in D. It's just that D requires that
they be marked as shared. That being said, how many objects should be
protected by a given mutex depends entirely on what you're doing. In some
cases, it makes sense to protect a lot of objects with the same mutex (e.g.
all of the member variables of a class could be protected with a single
mutex, which is what would happen with synchronized functions/classes), and
in other cases, it makes sense to have as many as a mutex per variable.
Having fewer mutexes is easier to handle, but it can also mean that code
gets blocked waiting more. And of course, in some cases, the state in
question is really spread across multiple variables, and they all need to be
protected together. I really can't judge how many mutexes would be needed
without knowing what you're doing.

That being said, if you're dealing with a rendering loop where you have one
thread updating the information, and another thread rendering, you probably
want to be using double or triple buffering.

https://en.wikipedia.org/wiki/Multiple_buffering

Basically, you have one buffer (or object or group of objects or whatever is
holding the state) which is updated by one thread, and then when it's ready,
it's swapped with another buffer. So, only the swap would need to be
protected by a mutex, because each buffer would only be referenced by a
single thread at a time. So, you could probably do something as simple as
having a pointer/reference to the buffer (or whatever object is holding the
state), and you swap that. There are a variety of ways that you could do it,
but one way would be something like

//---- update loop ----
while(cond)
{
    // update data in backBuffer...

    // update done

    synchronized(mutex)
    {
        auto temp = cast(Buffer)sharedBuffer;
        sharedBuffer = cast(shared Buffer)backBuffer;
        backBuffer = temp;
    }
}

//---- render loop ----
while(cond)
{
    // render...

    // wait...

    synchronized(mutex)
    {
        auto temp = cast(Buffer)sharedBuffer;
        if(temp !is frontBuffer)
        {
            sharedBuffer = cast(shared Buffer)frontBuffer;
            frontBuffer = temp;
        }
    }
}

This isn't necessarily the best way to do it, but in this particular scheme,
you have 3 buffers so that the render loop always has something to render
from while the update loop can keep swapping buffers as it updates and isn't
stuck waiting for the render loop to be done with the buffer, whereas the
render loop can always just render from the same buffer as long as there
isn't an update.

This does end up locking in a somewhat different way from the basic example
I gave before, because it's just the swap that's being protected, with each
thread passing ownership of a buffer via the middle/shared buffer. However,
it does rely on making sure that all references to the buffer and everything
that it references are passed to the other thread each time so that
operating on a buffer outside of the area protected by the mutex doesn't
risk operating on anything could be being accessed by the other thread.

Essentially, you can use whatever scheme would make sense in C/C++. You just
have to cast away shared in the area protected by the mutex. Everything
else is basically the same, including making sure that either the
unprotected data is only ever accessed by one thread or that all accesses
which could occur on both threads be protected by a mutex.

- Jonathan M Davis



More information about the Digitalmars-d-learn mailing list