Making a Callable for multithreading inside a function and it doesn't update parameter from that function, only globals can be updated

Godot Version

v4.4.1.stable.steam [49a5bc7b6]

Question

I tried making for cycle multithreaded, but I got this bug (or is this a feature?). First the code:

@tool
extends EditorScript


var mutex:= Mutex.new()
var threads_pool: Array[Thread] = []


func _run() -> void:
	var threads_count = OS.get_processor_count()
	for t in threads_count:
		threads_pool.append(Thread.new())
	
	var n:= 0.0
	var f = func() -> void:
		mutex.lock()
		print(n, " before")
		n += 1
		print(n, " after")
		mutex.unlock()
	
	for i in range(0, 100, threads_count):
		print(n)
		for t in threads_count:
			if i + t >= 100:
				break
			threads_pool[t].start(f.bind())
		for t in threads_count:
			if i + t >= 100:
				break
			threads_pool[t].wait_to_finish()
	print(n)

Variable n is recognized by f Callable, because it will give error if i remove:

var n:= 0.0

But when the threads are running it sees n, but each new thread sees it as 0.0, it never gets updated.
If I make n global it will update, is it a bug or a feature I need to learn??

So I tried to store n inside an array and it works normal:

var n:= [0.0]
	var f = func() -> void:
		mutex.lock()
		print(n[0], " before")
		n[0] += 1
		print(n[0], " after")
		mutex.unlock()

But I can’t figure out if it’s normal that int, float, and Vectors don’t work, but pointer structures do?

Just curious, what happens if you pass n to the f callable.

@wyattbiker
As float doesn’t update n in function, as array updates n, same as in my code.

Lambdas capture by the same logic other variable assignments do. If the value doesn’t have .duplicate() then it will be copied by default, such as float, int, Vector2, most anything not RefCounted or Nodes. You probably get this relevant script warning:

(CONFUSABLE_CAPTURE_REASSIGNMENT):Reassigning lambda capture does not modify the outer local variable “n”.

1 Like

@gertkeno
You are right, it shows the warning.
So it is normal, but still it’s kinda sad that i need to use arrays to make it work for me.
Thanks!

If you want to use something cleaner than array subscripts use a class object. E.g.

### your other code here...

class Foo:
	var n:float = 0

func _ready():
	var threads_count = OS.get_processor_count()
	print(threads_count)
	for t in threads_count:
		threads_pool.append(Thread.new())
	
	var obj := Foo.new()
	
	var f = func() -> void:
		mutex.lock()
		print(obj.n, " before")
		obj.n += 1.0
		print(obj.n, " after")
		mutex.unlock()
	
	for i in range(0, 100, threads_count):
		print(obj.n)
		for t in threads_count:
			if i + t >= 100:
				break
			threads_pool[t].start(f.bind())
			
		for t in threads_count:
			if i + t >= 100:
				break
			threads_pool[t].wait_to_finish()
	print(obj.n)