@Adám
Command Aliases:
⍞← -> #TIO do apl-dyalog {0::⎕←⊃⎕DM⋄⎕←%args%}⍬
⎕← -> #TIO apl {0::⎕←⊃⎕DM⋄⎕←%args%}(⎕NS⍬).⍎'⎕CY''salt''⋄⎕SE.UCMD''←box on -f=on -t=tree''⊣enableSALT⋄⍬'
Language Aliases:
apl-dyalog -> [apl]
Message Aliases:
)help -> %handle% [Dyalog APL Language Elements](help.dyalog.com/16.0/Content/Language/Introduction/…)
)ref -> %handle% [Dyalog APL Reference Card](docs.dyalog.com/16.0/ReferenceCard.pdf)
)about -> %handle% You can evaluate an APL expression by typing it into chat prefixed by ⍞←. Use ⎕← instead for boxed display and multi-line r…
#tio alias message )about %handle% You can evaluate a single line of APL by typing it into chat prefixed by ⍞←. Use ⎕← instead for boxed display and multi-line results and use ⋄ instead to silence the first statement. Do not use markdown, but fixed-width (4 initial spaces) is fine. Commands: )lb for language bar, )help for table of language elements, )docs for full documentation, )ref for PDF reference card, )idioms for idiom list.
@Adám You can evaluate a single line of APL by typing it into chat prefixed by ⍞←. Use ⎕← instead for boxed display and multi-line results and use ⋄ instead to silence the first statement. Do not use markdown, but fixed-width (4 initial spaces) is fine. Commands: )lb for language bar, )help for table of language elements, )docs for full documentation, )ref for PDF reference card, )idioms for idiom list.
If you haven't had a play with Dyalog APL at TIO (Try It Online) then take a look at Leigh Mercer's mathematical limerick expressed in APL https://tio.run/##TU8xbsJAEOx5xdTYQOwYoRQUKGnoKPKBI7c2J9l35M62ZB5AEQmUJqJKlUf4Af6JPwJrAyJTzUozOzNim45kJVKTnBm23X97kyBEEEUIn4D28IsFpNmR9iGQWOMcEy35cB/Gkj@wHpuem1M0bOoQPTrXKi0c8o0lQq4y6jjBfRbCEqwxOUyM2BSWHzQ1f5jhgc7/pkolSWJdwVHJ8degaXMKgv@6PidW5T2G0pt6/jIM0X79oT3@vL6z9TZn6aCVvneR/RjNfQTWKkfGm8YX
That's blown the diet!!! Thanks for the gift @BPBAPLer - 4 different Reese's Peanut Butter Cups - and you have to try one of each to see which one you like best :-) https://twitter.com/Jasey_Wasey/status/986610772854886400
Today's subject is multi-threading using APL threads.
Tomorrow, CXO Morten Kromberg will be presenting multi-threading using OS threads on dyalog.tv.
OK, so what is an APL thread?
Dyalog APL is allotted processor time by the operating system, and normally pretty much all that processor time is dedicated to computing whatever expressions the user (or his program) throws at APL.
The session will be locked while APL computes, and then the result (if any) is displayed.
However, sometimes you want to have some program running in the background while you do other stuff, or you want to run multiple programs at the same time, each e.g. monitoring various events happening in the environment.
You could also have two simultaneous GUI programs so that the end-user may interact with each one independently.
For all this, Dyalog APL provides APL threads, where the interpreter makes sure to slice the available processor time between your tasks, much in the same way as the OS slices processor time to the various programs running on the computer.
The mechanism is quite neatly integrated with the language in the form of a monadic operator &. You may recognise this symbol from UNIX-like OSs where the addition of a & at the end of the command line executes the command in a separate thread.
Yes. and it isn't a coincidence. Indeed, as a monadic operator, it goes to the right of the function you are calling: foo&
So foo& is a derived function which is just like foo but executes in a separate thread. Just like any other derived function you can assign this to a separate name, etc.
However, remember that you can't apply an operator to a niladic function, so if want to call a niladic function with &, you need to enclose it in a non-niladic function, and give it a dummy argument: {niladic}&'dummy'
Started waiting 2 s
Unrelated stuff
Elapsed: 2.006793 s
So what happened here?
{⎕←'Started waiting',⍵,'s' ⋄ ⎕←'Elapsed:',(⎕DL ⍵),'s'}&2 spun off a thread with the dfn outputting the starting message, then waiting 2 secs, then outputting the elapsed message.
Meanwhile, execution continued with waiting a second, and then outputting the unrelated stuff.
@H.PWiz It could be smaller. Just wanted to make it perfectly clear what happens, and if you try this in your local APL session, the visual effect is neater.
So, since fn& is just another (derived) function, you can of course apply it to anything you want, just like any other function.
@Adám I'm still a little confused. If I use some timeconsuming function to "give the dfn time to act", It still happens before the dfn: https://tio.run/##SyzI0U2pTMzJT///v/pR39RHbRPUg0sSi0pSUxTKEzNLMvPS1XUe9W7VUS9WV3jU3aIAVeOak1hQnJpipa6jARRx8VEAqtEEKapVM9Az5IKo0tAwfNQx4/B0zUe9iw0NIEATyPj/HwA
@Adám I didn't mean to delete my comment, but the execution time on that TIO link is >1s, while the dfn only takes 0.1s of that time. I think 1s should be long enough
@H.PWiz It does so between lines, but not more often than a certain setting allows it to.
Btw, this means that you can ensure that multiple statements are not interrupted by putting them on the same line with diamonds instead of line breaks.
It also switches threads during ⎕DL, so this also answers why we need ⎕DLs in our examples.
@Adám I take it more like the two statements were running against each other in a marathon and the second finished entirely before the first even got to the first step :P
Calling 'ABC' f⍣5&¨ 0.2 0.6 0.8 will spawn three fs, each looping 5 times, each waiting various times in each of its loops. and outputting its label and the global variable result in each loop.
OK, let's take an example where that threads don't just blindly do something, but rather make a decision to do something or continue based on the circumstances at the time.
We'll use f←{0=⍺|N ⊣ ⎕DL ⍵:r←⍵ ⊣ ⎕←⍺,'divides',N ⋄ r←⍵}
So it waits a bit, then checks whether the global N is divisible by its left arg, and if so, outputs a message, otherwise it just quits. The r← is to make the function shy. We return ⍵ to make ⍣ apply to the same argument again.
So now we'll start a thread which runs for a second, outputting numbers every tenth of a second, but after 0.7 seconds, we'll check whether it has finished (i.e. isn't on the list) and if it is, we kill it, otherwise we say it has finished: {⍵∊⎕TNUMS:'Timed out'⊣⎕TKILL ⍵ ⋄ 'Finished'}
@quartata No, they do not. They are all inside a single OS thread — that of the APL process. To learn how to do all this in separate OS threads, watch tomorrow's Webinar.
Yeah, if the thread has already finished by the time you call ⎕TSYNC, then it doesn't have a value, and you may get a value error if you try to use the result. You'll have to trap that or make sure to call it in time.
Naturally, as APL is an array language, you can give ⎕TSYNC an array of thread numbers. The result will have the same shape as the array of thread numbers, with each corresponding element being the result of that thread. ⎕TSYNC will wait for the last necessary thread to finish.
@Pavel You made t into a derived function, and then you used that to create an atop.
It's nice that TSYNC can just, like, take an array and wait on all of the tasks. In Python you have to use some more uglier functions, like asyncio.gather.
APL has automatic detection of deadlocks, i.e. when a set of threads are all awaiting each other so that none can continue. ⎕TSYNC 0 says "wait for the main APL thread to finish", but APL knows that won't happen, so it throws a DEADLOCK exception right away.
Of course you can trap deadlock exceptions and handle them appropriately.