Explain the situation with multi-threading, now it seems to me a little confusing. With GC: refc heaps are local for each thread. Therefore, it is only possible to copy shared data, or manual control via ptr (which is unsafe). With GC: ARC/ORC heap is shared so data can be moved. ARC doesnt use atomic counters so you need to manually lock data(which is eventually unsafe). Another option is to use channels with data copying as in refc.
Channels have a number of disadvantages: "Note: This is part of the system module. Do not import it directly. To activate thread support compile with the --threads:on command line switch. Note: Channels are designed for the Thread type. They are unstable when used with spawn Note: The current implementation of message passing does not work with cyclic data structures. Note: Channels cannot be passed between threads. Use globals or pass them by ptr."
There are also isolates(https://github.com/nim-lang/RFCs/issues/244), which are an evolution of the idea of owned ref. I'm not sure I understand correctly what is meant by "pass subgraphs to threads", what is subgraph? But in general, this is an opportunity to put data in a container that will check that a number of restrictions for that data are not violated, which guarantees the safe movement of data into channels (instead of copying). And most importantly, they are not ready either.
So the question is, what ways to work with shared data are relevant for ARC/ORC right now?
I've heard that it's advised to use the new channels with ARC/ORC: https://github.com/nim-lang/threading.
This should be at the top of the stdlib channels docs, in my opinion.
To create memory that can be shared, you need to ensure that the memory backing the data processed in another thread is valid through that processing.
You can ensure that with:
You can get away with no synchronization primitive if your algorithm is guaranteed to never touch the same memory at the same time, typically this is a parallel for. This is called Data Parallelism.
When memory might cause conflict, you can use a lock to ensure consistent ordering of operations and avoid corruption.
Lastly, you can also ensure that any one point in time, only one thread has visibility on an object, its owner, and ownership is passed from "service" to "service" each running on their threads, basically a thread-level microservice architecture or producer-consumer architecture that communicate via channels.