I'm working with Julia. The IDE is Juno.
If I'm right, #async can generate a task, it's just like a thread.
So we can do this:
#async begin
# do something1
end
#async begin
# do something2
end
Now, I need to lock a thread. For example, do something1 is to push message to a list and do something2 is to pop message from the same list.
It's like synchronized in Java.
what is synchronized in julia?
To keep a block mutex:
mutex = RemoteRef()
#async begin
put!(mutex, true)
# do something1
take!(mutex)
end
#async begin
put!(mutex, true)
# do something2
take!(mutex)
end
There is also a #sync macro:
help?> #sync
Wait until all dynamically-enclosed uses of #async, #spawn, #spawnat
and #parallel are complete. All exceptions thrown by enclosed async
operations are collected and thrown as a CompositeException.
#sync #async begin
# do something1
end
#sync begin
# some code
#async begin
# do something2
end
#async # do something 3
end
Related
This is my idea
task=#task begin
while true
sleep(1)
foo()
if should_end
# ?
end
end
end
But there are some problems
are there any simple ways to contact other than using global should_end?
how to end the task from inside the expression?
While implementing something like this can be a good exercise, note that Timers are in the standard library and they are similar to the JS functions you may be used to because Julia and Node.js both use libuv internally. Whether you use Node.js setInterval or Timeout, or Julia Timer eventually uv_timer_start is called (although the creation and low-level management of timers is different in the respective runtimes).
To answer your 2nd question, you can just break in the if expression:
julia> should_end = false
false
julia> task = #task begin
while true
sleep(1)
if should_end
break
end
end
"Tasks can return a value"
end
Task (runnable) #0x00007fca1c9c3990
julia> schedule(task)
Task (runnable) #0x00007fca1c9c3990
julia> task
Task (runnable) #0x00007fca1c9c3990
julia> should_end = true;sleep(1)
julia> task
Task (done) #0x00007fca1c9c3990
julia> fetch(task)
"Tasks can return a value"
As for the 1st question, there is a lot of information in the Julia docs, e.g. the Asynchronous Programming section. As is described there, Channels can be used for communication between tasks (when suitable). Note that should_end doesn't have to be global. A task wraps a function and that function can capture variables in enclosing scopes (#task begin a = 1 end is really just Task(() -> begin a = 1 end).
Here is a short example using a Timer and a Channel to send data to a task:
function pinger(n)
ping() = parse(Float64, match(r"time=(.*) ms", read(`ping -c 1 8.8.8.8`, String))[1])
ch = Channel{Float64}(0)
pinger = Timer(0.0; interval=1.0) do timer
put!(ch, ping())
end
summer = #task begin
total = 0.0
count = 0
while count < n
total += take!(ch)
count += 1
end
total / count
end
schedule(summer)
fetch(summer)
end
julia> pinger(3)
19.5
I am trying to understand the usage of #sync and #async macros in Julia. I am trying to get this MWE to work and the program does not terminate. Any help is appreciated.
function process_node(nodes, id)
#show id
sleep(1.0)
nodes[id] = true
return
end
function main()
nodes = Dict( i => false for i in 1:10 )
jobs = Channel{Int}(15)
for i in 1:10
put!(jobs, i)
end
#sync for id in jobs
#async process_node(nodes, id)
end
println("done")
end
main()
The program never gets to the line println("done"). I do not know why.
Thanks in advance.
There is nothing wrong about the use of #sync and #async in this example.
the loop for id in jobs never return because it blocks forever waiting endlessy for values no more inserted into the Channel.
Directly from the docs:
The returned Channel can be used as an iterable object in a for loop, in which case the loop variable takes on all the produced > values. The loop is terminated when the channel is closed.
One solution is to signal the end of the streams of values with a special Int value, for example -1 or if this is not possible with a nothing value, declaring jobs as Channel{Union{Int, Nothing}}.
function main()
nodes = Dict( i => false for i in 1:10 )
jobs = Channel{Int}(15)
for i in 1:10
put!(jobs, i)
end
put!(jobs, -1)
#sync for id in jobs
if id == -1
close(jobs)
else
#async process_node(nodes, id)
end
end
println("done")
end
To help my debugging (and also in order to better understand how Julia macros
work), I'm trying to define a simple macro that sourrounds blocks of code with
"Entering" and "Leaving" notifications. Here is what I've come up with so far:
macro dbg(block_title, expr)
quote
title = $block_title
println("Entering $title")
$expr
println("Leaving $title")
end
end
At first glance, it seems to do what I want:
julia> #dbg "first test" begin
println("does it work?")
end
Entering first test
does it work?
Leaving first test
however, as soon as variables are involved, nothing works anymore and I get
UndefVarError for all variables accesses. It looks like
the scope inside and outside the macro are distinct:
julia> #dbg "initialization" begin
foo = rand(10)
println("foo = ", foo)
end
Entering initialization
foo = [0.9178016919066918, 0.6004694971609528, 0.5294790810682284, 0.04208146400653634, 0.09271603217172952, 0.2809448815925, 0.68236281020963, 0.8313876607106496, 0.07484095574744898, 0.14099531301938573]
Leaving initialization
julia> foo
ERROR: UndefVarError: foo not defined
What am I doing wrong?
In short, you're missing the notion of macro
hygiene and
in particular the esc
function.
Although this part of the documentation is a good read for anyone wanting to develop their own macros, let's try to expand a little on what macro hygiene does in this particular example, and how you can fix things.
A useful way to debug macros is provided by
#macroexpand:
julia> #macroexpand #dbg "initialization" begin
foo = rand(10)
println("foo = ", foo)
end
quote
#= REPL[1]:3 =#
var"#32#title" = "initialization"
#= REPL[1]:4 =#
Main.println("Entering $(var"#32#title")")
#= REPL[1]:5 =#
begin
#= REPL[5]:2 =#
var"#33#foo" = Main.rand(10)
#= REPL[5]:3 =#
Main.println("foo = ", var"#33#foo")
end
#= REPL[1]:6 =#
Main.println("Leaving $(var"#32#title")")
end
Leaving aside all comments within #= ... =# markers, we see almost the code we
wanted to get: the user code block has been surrounded with statements printing
the "Entering" and "Leaving" notifications. However, there is one notable
difference: variable names like foo or title have been
replaced with weirdly looking names like var"#33#foo" or var"#32#title". This is what is called
"macro hygiene", and it helps avoiding clashes that could occur between the
variables used in the macro itself (like title in this example), and the variables used in the code block
provided as an argument to the macro (like foo here). (Think for example what
would happen if you used you #dbg on a code block that defines a title variable.)
Julia errs on the side of caution, and protects in this way all variables
appearing in the macro. If you want to disable this for selected parts of the
expression produced by the macro, you can wrap these subexpressions inside the
esc
function. In your example, you should for example escape the user-provided
expression:
macro dbg(block_title, expr)
quote
title = $block_title
println("Entering $title")
$(esc(expr))
println("Leaving $title")
end
end
Now things should work like want them to:
julia> #dbg "initialization" begin
foo = rand(10)
println("foo = ", foo)
end
Entering initialization
foo = [0.2955287439482881, 0.8989053281359838, 0.27751430906108343, 0.4920810199867245, 0.7633806735297282, 0.34535540650110597, 0.7099231627594489, 0.39978144801175564, 0.9104888704503833, 0.1983996781283539]
Leaving initialization
julia> #dbg "computation" begin
foo .+= 1
end
Entering computation
Leaving computation
julia> foo
10-element Array{Float64,1}:
1.295528743948288
1.8989053281359838
1.2775143090610834
1.4920810199867245
1.7633806735297282
1.345355406501106
1.709923162759449
1.3997814480117556
1.9104888704503833
1.198399678128354
I recently started Ada programming and now I'm stuck.
I created a program with multiple tasks. The main-task is managing incoming communication and as a consequence starts working-tasks or transfers data to the working-tasks.
The working-tasks are all of the same kind but with different identifiers.
They do their work and should finish after that. For example:
task body Access_Protected is
begin
accept Start(foo: in Integer; foo2: out Integer)
do something
end Start;
while Go_loop loop
select
accept Quit do
Go_loop := false;
end Quit;
or
accept Insert(foo3: in Integer)
do something
if something = 0 then
Go_loop := false;
end if;
end Insert;
or delay 2.0;
end select;
end loop;
end Access_Protected;
I understand that the working-task should be terminated when the Go_loop is finished. Am I right?
It works to start the task one time but when the main-task tries to restart the working-task by calling the Start procedure, nothing happens.
Can someone please tell me which point I am missing.
A task and subprogram are somewhat related in that when the body is completed the construct ends, this is to say that the construct ends with it's appropriate end; in the case of a procedure control returns to the caller, in the case of a function the exception PROGRAM_ERROR is raised, and in the case of a task the controlling "thread" terminates.
What's happening in your particular problem, it seems, boils down to the following:
Package Example is
Task Type Message_Task is
Entry Execute;
End Message_Task;
End Example;
Package Body Example is
Task Body Message_Task is
Use Ada.Text_IO;
Begin
accept Execute do
Put_Line( "Rendezvous!" );
end Execute;
delay 0.2; -- Stub delay.
Put_Line( "Finishing Task." );
-- Task Ends Here.
End Message_Task;
End Example;
--...
Test : Example.Message_Task;
--...
Test.Execute;
-- Test.Execute can't be accepted here because it can only accept "Execute"
-- the one time, as per the body's definition.
The reason that this really is like your problem is because, likewise once you say "X.Start(1,2)" another call to Start doesn't reset the position of the task's execution back up to that accept.
If you wanted the task to "stay alive" for further processing you could do one of two options.
Option 1 -- set up a 'protocol':
Package Example is
Task Type Message_Task is
Entry Initialization;
Entry Execute;
Entry Quit;
End Message_Task;
End Example;
Package Body Example is
Task Body Message_Task is
Use Ada.Text_IO;
Has_quit : Boolean := False;
Begin
Main:
loop
select
accept Initialization do
null;
end Initialization;
accept Execute do
null;
end Execute;
or
accept Quit do
Has_Quit := True;
end Quit;
end select;
Exit Main when Has_Quit;
end loop Main;
End Message_Task;
End Example;
Option 2 -- Allow termination.
Package Example is
Task Type Message_Task is
Entry Initialization;
Entry Execute;
End Message_Task;
End Example;
Package Body Example is
Task Body Message_Task is
Use Ada.Text_IO;
Has_quit : Boolean := False;
Begin
accept Initialization do
null;
end Initialization;
Main:
loop
select
accept Execute do
null;
end Execute;
or
terminate;
end select;
end loop Main;
End Message_Task;
End Example;
The subtle difference is Option 2 gets rid of the Quit entry, allowing the task to 'rest' on the terminate alternative while Option 1 is more explicit in control (and required in some cases), but requiring that Initialization & Execute be called in pairs.
A task only runs until it reaches the end of its main sequence of statements (ignoring various technicalities).
If you want a task to do something, and then pause until it receives an external trigger, you should put a loop around the statements you have in the task body.
Here's some Julia code:
addprocs()
#everywhere begin
type Test
values::Array{Any,1}
addValue::Function
function Test()
this = new()
this.values = Any[]
this.addValue = function(v)
push!(this.values,v)
end
return this
end
end
end
#everywhere t = Test()
#everywhere t.addValue(myid())
#spawnat 2 whos()
r = #spawnat 2 t
I believe this code defines a Test type on all processes and then creates an instance of that type stored in a variable, called t, on each process. This variable is local to that process. I then use one of the Test methods run in parallel on each process to mutate the local instance of Test. In the line
#spawnat 2 whos()
we can see that the local t has indeed been updated. However, I get a HUGE error when trying to fetch any of the remote variables t into a RemoteRef. The error message is quite large, but it leads me to believe that the Julia serialization process cannot handle user-defined types. Can anybody lend some insight? Thank You!
Upadate:
A simpler example yielding the same error:
addprocs()
#everywhere begin
type Test
values::Array{Any,1}
addValue::Function
function Test()
this = new()
this.values = Any[]
this.addValue = function(v)
push!(this.values,v)
end
return this
end
end
end
t = Test()
R = RemoteRef(2)
put!(R,t)
I don't know why trying to fetch that composite object with a function field inside, causes the Error..., anyway I encourage you to use Julia way for creating the same functionality:
First of all you don't need to include addValue function inside the Test type, try to separate data storage from functionality:
#everywhere begin
type Test
values::Array{Any,1}
addValue::Function
function Test()
this = new()
this.values = Any[]
return this
end
end
end
#everywhere function addValue(v,t::test)
push!(t.values,v)
end
then initialize t everywhere:
#everywhere t = Test()
and mutate t everywhere:
#everywhere addValue(myid(),t)
Secondly run getfield() command on desired process to get local variable:
r = #spawnat 3 getfield(Main,:t)
check the result:
assert(fetch(r).values==[3])
for more details about how to pass variable between processes check this question.