Background thread, async and GUI (dlangui)

frame frame86 at live.com
Thu Jul 21 17:26:07 UTC 2022


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



More information about the Digitalmars-d-learn mailing list