Does anyone understand how to use "shared" types with concurrency send/receive functions?
Arek via Digitalmars-d-learn
digitalmars-d-learn at puremagic.com
Mon Aug 14 12:49:30 PDT 2017
On Monday, 14 August 2017 at 03:59:48 UTC, Jonathan M Davis wrote:
> On Sunday, August 13, 2017 16:40:03 crimaniak via
> Digitalmars-d-learn wrote:
>> More of this, I think, you can't avoid __gshared for any
>> complex work. Even mutexes from Phobos doesn't support shared,
>> so I had to 'cowboy with __gshared' when implementing my site
>> engine.
>
> The way to handle shared is to protect the section of code
> that's using the shared object with either a mutex or
> synchronized block, and then you cast away shared from the
> object within that section and operate on it as thread-local.
> When you're done, you make sure that you don't have any
> thread-local references to the data, and you release the mutex
> or exit the synchronized block. e.g. something like
>
> shared T sharedObj = getSharedObj();
>
> synchronized(mutex)
> {
> T nonSharedObj = cast(T)sharedObject
>
> // do stuff...
>
> // make sure that no references to nonSharedObj have escaped
> }
>
> // now, there's just the shared version of the object
>
Yeah, and this is what i'm doing now (more or less).
To be more precise, I don't even want to synchronize access to
the shared resource between the threads. I just want to move the
object from one thread to another.
Of course I could copy the local object, but my obcjecs has
indirect references to others, forming kind of tree.
I like the idea of channels in Go. I've tried to get something
similiar with send/receive.
In this case, object could be also immutable, because once there
are created (in the deserialization process) they will no be
modified. I just have to emit them into another task (to be
honest, I use fiber, so it's not even another thread, and they
will not be accessed in parallel).
But all this language protections makes the issue unexpectedly
complicated.
> And no, this isn't ideal, but the only semi-decent solution
> that's been proposed that safely casts away shared for you is
> synchronized classes, which Andrei describes in TDPL but have
> never been implemented. And because they can only safely strip
> off the outermost layer of shared, they're of questionable
> usefulness anyway. Ultimately, even with synchronized classes,
> in many situations, the programmer is going to have to
> carefully cast away shared to operate on the object within a
> protected context.
>
> Now, the fact that the mutex objects don't handle shared
> correctly is another issue entirely. Having to cast away shared
> from mutexes is dumb, because you're obviously not going to be
> protecting them with a mutex, and their operations have to be
> atomic anyway for them to do what they do. So, that definitely
> needs to be fixed. However, I believe that it _has_ been fixed
> in master, and it might have made it into a release now, but
> I'm not sure. So, core.sync.mutex.Mutex _should_ now be useable
> as shared like it should be.
>
> In general though, the idea is that you simply don't operate on
> shared objects except via atomic operations. Otherwise, you
> risk concurrency problems. And really, this is the same as what
> you'd do in C/C++, except that in C/C++, it doesn't catch you
> when you operate on an object that's shared across threads with
> non-atomic operations (because the object isn't explicitly
> typed as shared), and you don't have to cast away shared to do
> non-atomic operations. So, having to cast away shared is the
> price of getting the protection against accidentally using
> non-atomic operations on a shared object as well as the price
> we pay to be able to have the type system distinguish between
> shared and thread-local objects so that it's able to optimize
> based on the knowledge that an object is thread-local.
> Ultimately though, you're doing the same thing that you'd do in
> C++ if you're handling concurrency safely. You just have to
> explicitly mark stuff as shared and carefully cast away shared
> in certain, protected contexts.
>
> Using __gshared in extern(D) code is just asking for it,
> because then you have an object that the compiler thinks is
> thread-local but isn't, and you risk subtle and nasty bugs as a
> result. __gshared is only intended for binding to extern(C),
> global variables. To an extent, you can get away with using it
> with extern(D) variables, but that's not its intended purpose,
> and you risk running afoul of the compiler and what it chooses
> to do based on the assumption that the object is thread-local.
>
> - Jonathan M Davis
Thanks for the explanation! It would be good to have a
comprehensive article on this subject.
Arek
More information about the Digitalmars-d-learn
mailing list