How to return in-place two variables from fetch?

I have two issues please:
1- How can I mutate in-place x_1&x_2 by the returns of fetch ( Z and Y ) to avoid the below error?

A = [1,2,3,4,5]
x_1 = zeros(5); x_2 = zeros(5);
thr = Threads.@spawn begin
  Z = A .+ 1;
  Y = A .+ 2;
  Z, Y
end
x_1, x_2  = fetch(thr); # it works
x_1, x_2 .= fetch(thr); # it gives error
ERROR: MethodError: no method matching copyto!(::Tuple{Vector{Int64}, Vector{Int64}}, ::Base.Broadcast.Broadcasted{Base.Broadcast.Style{Tuple}, Tuple{Base.OneTo{Int64}}, typeof(identity), Tuple{Tuple{Vector{Int64}, Vector{Int64}}}})
Closest candidates are:

2- Another issue related to variable scope. Why Z&Y were not updated in the snippet code in case-1 while they were updated when the code was put in function f in case-2?

# Case-1
##### snippet Code
Z = zeros(5); Y = zeros(5);
thr = Threads.@spawn begin
  Z = A .+ 1;
  Y = A .+ 2;
end
fetch(thr)
# Here Z,Y are not updated (still zeros)
##### snippet Code

# Case-2
function f(A)
  ##### snippet Code
  Z = zeros(5); Y = zeros(5);
  thr = Threads.@spawn begin
    Z = A .+ 1;
    Y = A .+ 2;
  end
  fetch(thr)
  # Here Z,Y are updated
  ##### snippet Code
  Z, Y  
end
Z, Y = f(A)

The @spawn introduces a local scope block, if you want to assign to globals from local scopes, you need to use global to do so. But ideally, don’t assign to globals at all. Note that you are not mutating Y and Z but reassigning newly allocated arrays to those bindings.

2 Likes

Your first problem has nothing to do with threads or fetch. The error tells you that the broadcast doesn’t know how to copy the contents of the right hand side, a tuple, into the left hand side, a tuple too. That makes sense because tuples are immutable.

Either stick with the assignment, or better yet, write your threaded code to update x_1, x_2 in place.

Because Y,Z are local to the task created by @spawn. There is an anonymous function created that wraps the code. Within that function, e.g. Z = ... is a local assignment and shadows the global variable. You need to annotate with global

Z = zeros(5); Y = zeros(5);
thr = Threads.@spawn begin
  global Z = A .+ 1;
  global Y = A .+ 2;
end

The task created in the function on the other hand is actually a closure capturing the local A,Y,Z.

2 Likes

Thanks for your reply.

How can I update x_1, x_2 in place in the threaded code?

So function f actually makes the locals of Z,Y inside the task created by @spawn seen in its scope, right?

julia> A = [1,2,3,4,5];

julia> x_1 = zeros(5); x_2 = zeros(5);

julia> function f!(Y,Z,A)
         Threads.@spawn begin
           Z .= A .+ 1;
           Y .= A .+ 2;
         end
       end
f! (generic function with 1 method)

julia> f!(x_1, x_2, A) |> wait

julia> x_1
5-element Vector{Float64}:
 3.0
 4.0
 5.0
 6.0
 7.0
1 Like

Ah, it is the same as using dot.
Thank you very much for your reply.