@macroexpand
only shows you what an expanded macro (which is an AST transform, it replaces syntax with other, custom syntax) looks like. Placing it in front of @spawn
only returns what that expanded form looks like, the code isn’t run anymore.
First, @sync
and Threads.@spawn
are only related insofar they’re both managing Task
s. The former is about tasks in general (though originally intended for single-threaded, asynchronous code using @async
, it also works with tasks spawned by @spawn
), the latter is for spawning a Task
on different threads other than the original one.
Second, @sync
captures all exceptions that occur in spawned tasks inside of itself, as documented by its docstring:
help?> @sync
@sync
Wait until all lexically-enclosed uses of @async, @spawn, @spawnat and
@distributed are complete. All exceptions thrown by enclosed async
operations are collected and thrown as a CompositeException.
So you’d only get the exception once @sync
determines that all enclosed tasks have finished. In your example, that wouldn’t ever be the case due to the infinite loop. If you want to get those exceptions earlier, you’ll have to try/catch
or wait(task)
and handle them appropriately. This may (depending on your usecase) require additional communication, locking and/or synchronization with your long running task, so I’d recommend structuring your long running task with periodic checks for messages in e.g. a Channel
, to communicate that it should e.g. terminate, change behavior…
In general though, I’d recommend checking if you can avoid only testing your program in the presence of multithreading. If possible, try to test & find edge cases on a single thread, and only use multithreading when you’re reasonably certain that you’ve handled what can go wrong.