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

```d
// 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.

```d
// 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.

```d
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

Reply via email to