4 ms·
One thing I wanted to add is that in golang, you end up passing context.Context to all asynchronous functions to handle cancellations and timeouts, so you “colo
by za3faran 3y ago
One thing I wanted to add is that in golang, you end up passing context.Context to all asynchronous functions to handle cancellations and timeouts, so you “color” them regardless. Java folks with structured concurrency have the right idea here.
- metaltyphoon 3y agoI don’t get how people that mentions “no color” don't get this: context.Context IS color.
- SmooL 3y agoIt's not _quite_ the same: you can't call async code from a sync context (hence the color issue), but I can always pass a "context.Background()" or such as a context value if I don't already have one.
- TheDong 3y ago> you can't call async code from a sync context (hence the color issue), but I can always pass a "context.Background()" or such as a context value if I don't already have one. You can always pass context.Background, in this metaphor creating a new tree of color. You can always call "runtime.block_on(async_handle)", in this metaphor also creating a new tree of color.
- throwy2342 3y agoGo doesn't have async/sync distinction, no keywords so the metaphor doesn't hold.
- pkolaczk 3y agoYou can always pass the async executor to the sync code and spawn async coroutines into it. And you can keep it in a global context as well to avoid parameters. E.g. there is `Handle::current()` for exactly this purpose in Tokio. Function coloring is just a theoretical disadvantage - people like to bring it up in discussions, but it almost never matters in practice, and it is even not universally considered a bad thing. I actually like to see in the signature if a function I'm calling into can suspend for an arbitrary amount of time.
- akvadrako 3y agoIt's not the same because you can have interleaving functions which don't know about context. Say you have a foreach function that calls each function in a list. In async-await contexts you need a separate version of that function which is itself async and calls await. With context you can pass closures that already have the context applied.
- TheDong 3y agoYou can do that with async functions too by having the caller use, for example in rust, 'runtime.block_on(async_handle)' You're talking about doing this: f1 := func() error { return nil } ctx_f1 := func(context.Context) error { return nil } fns := []func() error{f1, func() error { ctx_f1(ctx) }} Basically, writing an anonymous conversion function. In, say, rust, the equivalent in this analogy would be: let f1 = || -> Result<(), ()> { Ok(()) }; let async_f1 = async { || -> Result<(), ()> { Ok(()) } }; let fns = vec![f1, runtime.block_on(async_f1)]; That conversion function, 'runtime.block_on', doesn't really seem that different. The analogy still seems to hold.
- gpderetta 3y agoNo, because if the function that includes those lines is itself async, it will now block, while the equivalent go coroutine will still preserve the stackfull-asyncness. I.e. you can't close over the yield continuation in rust, while it is implicitly done in go. For a more concrete example: let's say you have a generic function that traverses a tree. You want to compare the leaves of two trees without flattening them, by traversing them concurrently with a coroutine [1]. AFAIK in rust you currently need two versions of traverse, one sync one async as you can't neither close over nor abstract over async. In go, where you have stackful coroutines, this works fine, even when closing over Context. So yes, in some way Context is a color, but it is a first class value, so you can copy it, close over it and abstract over it, while async-ness (i.e. stackless coroutines) are typically second class in most languages and do not easily mesh with the rest of the language. [1] this is known as the "same fringe problem" and it is the canonical example of turning internal iterators into external ones.
- throwaway894345 3y agoHow do you figure? What’s requiring my async function to take a context in the first place? Even if it takes one, what’s stopping it from calling a function that doesn’t take one? Similarly, what’s stopping a function that doesn’t take a context from calling one which does?
- cyberax 3y agoThis is a convention, and you also don't _have_ to do it. A function without a context can still be used just fine from any code, you just won't be able to cancel it.
- throwy2342 3y agoThe correct interpretation here is that golang chose explicit context passing where java chose implicit. It's similar to explicit DI vs implicits. the function coloring metaphor doesn't make sense since the calling convention is the same nor are there extra function keywords (`async` vs non-async).
- DSingularity 3y agoHow is passing in a context “coloring”? This is GC for goroutines — specifically long running ones.
- mftb 3y agoThis isn't true. I have use cases that don't require cancellations or timeout. The tasks I'm running don't involve the network, they either succeed or error after an expensive calculation.
- eptcyka 3y agoWhat's the point of multiplexing tasks on a particular core if the tasks don't do any I/O? It will be strictly faster to execute the tasks serially across as many cores as possible then.
- mftb 3y agoSee response to sibling comment.
- throwaway2037 3y agoThis is an interesting post. My understanding: Most of the use case for async code is I/O bound operations. So you fire off a bunch of async I/O requests and wait to be notified. Logically, I/O requests normally need a timeout and/or cancel feature. However, you raise a different point: The tasks I'm running don't involve the network, they either succeed or error after an expensive calculation. This sounds like CPU-bound, not I/O-bound. (Please correct me if I misunderstand.) Can you please confirm if you are using Go or a different language? If Go, I guess it still makes sense, as green threads are preferred over system threads. If not Go, I would be nice to hear more about your specific scenario. HN is a great place to learn about different use cases for a technology.
- mftb 3y agoI think I just responded too hastily. I am working in Go. There is file IO going on in addition to the calculation (which because of a NAS or whatever could also be network IO). As a practical matter I had never felt the need to offer cancellation or timeout for these use cases, but I probably should, so mea culpa.