Capturing by value in the thread

Steven Schveighoffer schveiguy at gmail.com
Sat Oct 19 03:35:26 UTC 2024


On Friday, 18 October 2024 at 12:07:24 UTC, Kadir Erdem Demir 
wrote:
> It seems [=] functionality C++ does not exist in D.

No, D does not have this functionality.

> By using a helper function I made that example work.
>
> ```d
>
> import std.stdio;
>
> auto localFoo(int x) {
>     return (){ return x;};
> }
>
> void main() {
>     int x = 10;
>
>     auto lambda = (int capturedX = x) {
>         writeln("Captured reference : ", capturedX);
>     };
>
>     auto localFoo = localFoo(x);
>
>     x = 20;
>     writeln(localFoo());// Outputs: Captured value: 10 --> That 
> is what I need
>     lambda();     // Outputs: 20
>
> }
>
> ```

D captures all closures via allocation onto the heap. Using 
`localFoo` is the correct way to do this.

For nested functions, it has a reference to the outer stack 
frame, this is different.

But your example is even different, a default parameter means 
"pass this parameter if none is passed". So your call to 
`lambda()` is treated as if you wrote `lambda(x)`. It's not 
captured on definition, it's passed on usage.

>
> But when I trying the same technique on std.Thread it does not 
> work. Always reference is being captured.
>
> ```d
>
>
> void InitCurrencies()
>     {
>         auto captureFuction(int index)
>         {
>             return (){
>                 auto localIndex = index;
>                 string[] currentChunk = 
> ChunkedExchangePairNames(localIndex);
>                 writeln("Thread index: ", localIndex, " pairs:
>  ", currentChunk.join(";"));
>                 foreach (exchangeName; currentChunk)
>                     Connect(exchangeName, WebSocketType.All);
>                 WebSocket.eventLoop(localLoopExited);
>             };
>         }
>
>         for(int i = 0; i < m_ThreadCount; i++)
>         {
>             auto work = captureFuction(i);
>             auto worker = new Thread({
>                 work();
>             });
>             m_workerList ~= worker;
>         }
>
>         foreach(worker; m_workerList)
>         {
>             worker.start();
>         }
>     }
>
> ```	
> writeln("Thread index: ", localIndex, " pairs: ", 
> currentChunk.join(";"))
> Always prints the last value of my for loop.

In this case, you are passing in a local anonymous function which 
uses the stack frame pointer to find the value stored at `work`. 
This value gets changed each time through the loop.

What you need to do is simply pass the work function into the 
thread:

```d
auto worker = new Thread(work);
```

This copies the lambda *by value* into the Thread, and then you 
can change work, it won't affect that copy.

Note that it is a very long standing issue that closures are not 
done based on the full scope of everything -- only the stack 
frame of the function is used.

-Steve


More information about the Digitalmars-d-learn mailing list