On Thursday, 21 July 2022 at 13:27:49 UTC, Bagomot wrote:
> I had this question: how can I get the value from the task
, like how I can get from the spawnLinked
(ownerTid.send
and receive
)?
I'm using a taskPool
through arr.parallel
, but it became necessary to collect the progress from all tasks into one variable.
In this case, I can't use spawnLinked
because the worker is mutable, I get the "Aliases to mutable thread-local data not allowed" error.
The module creators want you to prevent from non-threadsafe actions. Merging into one variable can be such a non-threadsafe action, but it has not to be.
For example, if you have an array of fixed length or an already allocated one and each worker thread uses a fixed index to write into this result array variable then this operation may be threadsafe because each thread writes in different positions in the memory and the variable itself doesn't change.
If you are depending to overwrite an value instead, the operation is not considered threadsafe. For example just adding 1 + 2. Adding a value to an associative array is also not thread safe (it may re-allocate or re-index it's data)
We have basic tools to achieve this task. One is to lock actions with a synchronized block so each thread need to wait the other thread to complete before it can continue to execute the particular code position - and the other one are atomic operations that provide a better performance for simple operations because they are lock-free.
Why it's needed to know this? Because you may run into situations where have to deal with it, even the compiler keeps silent about.
https://dlang.org/spec/statement.html#synchronized-statement
// parent:
int result;
// ...
// worker/parallel:
// each thread can only run this code if no other thread is currently running this code section => the OS is locking, this costs time
synchronized {
result += 1;
}
https://dlang.org/phobos/core_atomic.html#.atomicOp
Usage of atomaticOp
is simple, eg.
// parent:
shared int result;
// ...
// worker/parallel:
// lock free, D-runtime ensures for a threadsafe operation
atomicOp!"+="(result, 1);
Looking on atomicOp
you will see it want you to use a shared
variable. And this is an easy way to make the compiler happy if you have mutual data. If you know what you are doing (not making thread unsafe-operations) then it's fine to just cast your data to shared
that now can be sent or received.
tid.send(cast(shared)mutualData);
A better approach is always to avoid such things completely and design your workflow to send and receive only simple data types (or immutable ones, which is also fine).
> Also, I could make a new Thread
for each task, but I think this is a bad idea. Also, I don't know how to send messages from child thread to parent thread.
The messagebox system from std.concurrency
should also work with parallelism tools, meaning inside a worker thread use std.concurrency.ownerTid
to get the parent Tid.
Looking for your task you might want to use a WorkerLocalStorage
solution instead, look at the example:
https://dlang.org/phobos/std_parallelism.html#.TaskPool.WorkerLocalStorage