digitalmars.D.learn - Usage of Shared
- Colden Cullen (10/10) Feb 07 2014 Hi all,
- Stanislav Blinov (56/66) Feb 08 2014 I don't think you'll find many responses, mainly because 'shared'
Hi all, I've been trying to learn more about how the shared qualifier works, and so far I haven't been able to find much. I've read the "Migrating to Shared" article, as well as the shared section in "Attributes", but neither really give a good explanation on the details of what should be shared and what shouldn't. If anyone has any resources that they can share to help me (and others in the future) understand how and when to use shared it would be greatly appreciated. Thanks for the help!
Feb 07 2014
On Friday, 7 February 2014 at 19:58:21 UTC, Colden Cullen wrote:Hi all, I've been trying to learn more about how the shared qualifier works, and so far I haven't been able to find much. I've read the "Migrating to Shared" article, as well as the shared section in "Attributes", but neither really give a good explanation on the details of what should be shared and what shouldn't. If anyone has any resources that they can share to help me (and others in the future) understand how and when to use shared it would be greatly appreciated. Thanks for the help!I don't think you'll find many responses, mainly because 'shared' is not yet fully supported by the runtime or Phobos, so people don't use it all that much. In a nutshell, anything that is to be accessed concurrently from different threads should be shared. For example, here's an excerpt from one of my ongoing endeavours from the "Testing some singleton implementations" topic: --8<-- static void test(shared ulong* counter) { auto sw = StopWatch(AutoStart.yes); foreach (immutable dummy; 0 .. loopLength) { _thread_call_count += enforce(TestClass.get() !is null); } sw.stop(); atomicOp!"+="(*counter, sw.peek.hnsecs); } shared ulong timer; foreach (immutable i; 0 .. threadCount) spawn(&test, &timer); thread_joinAll(); immutable avgTotal = atomicLoad!(MemoryOrder.acq)(timer).to!double/threadCount; -->8-- Here, every thread measures how long its loop runs and then adds this time to a global timer. Naturally, the global timer is shared. But simply adding the qualifier is not enough. You also have to make concurrent access to your shared data safe. In this case, it's done using atomic operations from core.atomic. Note that in this particular example, I pass the counter into threads as pointer. I would prefer passing it by ref, but currently std.concurrency.spawn does not propagate changes to ref arguments. Normally, if you just want to pass data around between threads (primitive types, structs, any value type), where each thread will have its own copy of the data, it doesn't need to be shared. However, once you need to access the data concurrently, you have to make it shared and provide synchronization appropriately. References (class instances, pointers) are only allowed to be visible by several threads if they are shared. Thus, if you want to share a class instance, you'll need to create a shared constructor, and make all the relevant methods shared as well (while still maintaining thread safety in the methods' implementations). There are a couple of examples of shared lock-free data structures in Anrei Alexandrescu's "The D Programming Language" book (that particular chapter is published for free: http://www.informit.com/articles/article.aspx?p=1609144). There are still a lot of unresolved issues with shared (i.e. containers, existing runtime and Phobos types, destructors...), and the community is focused on other areas of the language, so for now you won't get much out of it. However, writing code is the best way of finding issues and inconsistencies. Given time, I hope the situation will improve.
Feb 08 2014