4 ms·
Nope, that's a fallacy. Sync and Async code have inherently different semantics and trying to unify them will always fail, no matter what technique you try to a
by valenterry 4y ago
Nope, that's a fallacy. Sync and Async code have inherently different semantics and trying to unify them will always fail, no matter what technique you try to apply. It has been tried more than often enough.
- unxdfa 4y agoCompletely agree with this. Having ported a lot of old non async c# to async, even mixing the notions for a moment is a disaster. This was a world of pain in the late .net classic lifecycle (4.5-4.8) where Microsoft kept fucking it up as well as us.
- foepys 4y agoIf you have legacy or UI code you are best served using Microsoft.VisualStudio.Threading to ease the pain. Not using it will land you in a world of random deadlocks and disappearing data from variables. vs-threading should be part of the .NET standard library but somehow isn't.
- pjmlp 4y agoHello Task.Run() scattered all over the place.
- unxdfa 4y agoI cried a little when I read this :(
- cube2222 4y agoGo did that, and it's working out very well. Agree with the grandparent. Async/await is unnecessary complexity in all but the most performance-oriented languages (like Rust). Very curious to see how Java Loom will pan out in practice.
- pjmlp 4y agoJava has a better approach, because you can still decide which kind of thread to use, which makes FFI performance still much better than cgo.
- oaiey 4y agoOnce you develop in a non single threaded ui framework it is not only about performance.
- cube2222 4y agoCould you please expand on that?
- oaiey 4y agoYou need to keep the UI responsive, so you hand off all resource or cpu heavy work off the UI thread, give back control to rendering and responding to user input ... and later need to collect the results in the UI thread to change the UI state. It is about doing things in parallel. There are many ways on how to achieve that, I am just nit picking on performance.
- cube2222 4y agoThen I think we misunderstood each-other. Go is asynchronous-first, it just doesn't use the (flawed) async-await semantic to model asynchronicity.
- metaltyphoon 4y agoNot really, look at doing FFI using Go. Its very slow compared to C# specially because of how Goroutines work.
- cube2222 4y agoTrading this for simplifying the remaining 99% of my code is a tradeoff I'm willing to make. The vast majority of projects don't use CGo, and of those that do, the majority doesn't call CGo in a tight loop, making the performance impact mostly irrelevant. So it's really coming back to the performance-orientedness of a language.
- he0001 4y agoI think that’s one of the reasons why Java went with virtual threads instead, as the Async/await pattern is hard to do? As that does give you asynchronous code without any of the coding hustle and hide it from the programmer and you can keep your APIs the same.
- throwawaymaths 4y ago> trying to unify them will always fail Wrong. Languages that definitely got it right: Erlang/Elixir (high level), especially elixir's Task.async Zig (low level) Very close runner-up: Go
- valenterry 4y agoErm, just look at the name: "Task.async". So there is the distinct concept auf async, otherwise why would you name it like that. Mind that I'm not talking about async/await syntax here.
- SirGiggles 4y agoNo there isn’t. Elixir, and by extension Erlang and the BEAM VM, doesn’t have the concept of stackless coroutines. Task.async/1 (or Task.async/3) at a high level just spawns another BEAM process links it to the currently running process, sets up a process monitor, and then runs. Task.await/2 just pulls from the mailbox of the current process for a returned message.
- valenterry 4y agoBut that is exactly what I mean. There is a semantic difference between just running a line of code or running ith with Task.async right? So then there is a distinct concept of sync vs async.
- SirGiggles 4y agoThe BEAM VM doesn't have faculties to accommodate any other form of concurrency other than synchronous, pre-empted green threads. Put simply, there is nothing other than synchronous in Elixir (edit: or Erlang or any other BEAM VM language). The Task module is basically just convenience functions (edit: convenience functions around existing BEAM VM functions for spawning BEAM processes or Erlang threads) meant to facilitate short term execution of, well, tasks. Task.async/3 and Task.await/2 are, from the thousand foot view, the equivalent of: parent = self() spawn(fn -> send(parent, do_something()) end) receive do result -> IO.puts result end In Elixir/Erlang parlance, I have just spawned a thread where the BEAM VM handles both the internal data structures to facilitate concurrent-safe communications between threads and the scheduling in userspace. Put more generally, the mentality of concurrency in Elixir/Erlang is the equivalent as that of traditional pthreads in C.
- pjmlp 4y agoYet David Fowler has been researching how to integrate Go and Java's approach into .NET.
- pron 4y agoCan you try and express what the semantic differences are exactly? Most approaches for expressing semantics yield equivalent results or nearly so. For example, using a Hoare triple approach, what conditions in P and Q would yield different assertion results between, assert P; foo(); // blocking call assert Q; and assert P; await foo(); // await on an async call assert Q; ? There may be some (e.g. thread identity is preserved in the former but possibly not in the latter, depending on implementation, and that could be expressed with proper assertions), but they only highlight how the differences are merely technical and certainly not "inherent".
- valenterry 4y agoIn your examples you are explicitly handling the async nature of foo(). So you acknowedlge that there is a difference and you even use specific syntax to deal with it. The question is rather: is it possible to remove the await and just automatically rewrite every program to use await where necessary? And the answer is no, because with only sync functions you have the guarantee that if your thread crashes, every code immediately stops executing. Not so with async functions. Once they are run there is a chance that your own thread crashes but some other thread keeps executing more code, at least for some time, potentially executing side effects. It cannot be automatically decided if this is okay or not. Or to turn it around: the behaviour of a program that automatically awaits async functions (which formerly where sync) can be different.
- pron 4y agoI think we're talking past each other. There is a semantic difference between `asyncFoo()` and `blockingFoo()`, but not between `await asyncFoo()` and `blockingFoo()`, and not between `asyncFoo()` and `spawn blockingFoo()`. There's obviously a difference between launching an operation to run concurrently with the spawning code and launching an operation and awaiting its completion, but that difference does not require the introduction of an async/await feature, and is perfectly expressible with nothing but threads and synchronous code. In particular, there is no need for two separate syntactic domains between async and sync subroutines. Indeed, in Erlang, Go, and Java, all the same behaviour of async/await is achieved with no such need, and no need for different kinds of APIs.
- serguzest 4y agoYou should elaborate. I just want to read a file, program flow should stop until it is done. sync or awaited async call for this semantically sounds exactly same to me. I knew Go can do it, so it doesn't "always fail"
- valenterry 4y ago> You should elaborate. I just want to read a file, program flow should stop until it is done. sync or awaited async call for this semantically sounds exactly same to me. What if you are reading the file and somewhere else in the code there is some logging happening. Should the logging flow stop until reading the file is done? Should it not? If not, what should happen if the "thing" (e.g. thread) that executes the code to read the file crashes? Go does not treat sync and async the same. It specifically has syntax to deal with async, e.g. go routines.