1,230 karma · joined December 9, 2011
Get in touch: hacker.news AT brunosutic.com
It seems you have more experience than me with async-related work. I'll keep an eye on this kind of scenario you brought up.
Sync do
results = []
Async do
sleep 10
results << "Hello"
end
puts results
endI find these races relatively easy to spot: yes, global state CAN change when you yield back to the reactor.
IMO thread race conditions are much, much worse. Global state can change AT ANY POINT, because thread scheduler preemptively switches threads. Here's an example:
global.a = 0
thread {
global.a = 1
print(global.a) // a is 1 or 2
}
thread {
global.a = 2
print(global.a) // a is 1 or 2
}
print(global.a) // a is 0, 1 or 2No, I don't think there's anything fundamentally different.
Ruby 3.0 implements a "fiber scheduler" feature that enables "colorless Async". Fiber scheduler is an obscure Ruby feature, but the end result is brilliant. It was also a huge amount of work.
Side note: Fiber scheduler was implemented by the same guy who created Async Ruby - Samuel Williams. This guy is the mastermind (and master-coder) behind this project.
In any case, I'm assuring you: getting the results "out of tasks" is trivially easy.
> Surely any real program will have all the work done inside a single top-level event loop.
I don't get this. Can you please explain more what you have in mind and I'll try to help clarify things.
Yes, you can also use `Async { work }` instead of `Async::Task.current.async { work }`.
Source link: https://digital-fabric.github.io/polyphony/faq/#why-does-pol...
> Polyphony “patches” some Ruby core and stdlib APIs, providing behavioraly compatible fiber-aware implementations
Can you get more specific please?
My experience is 100% from Ruby where I've worked heavily with threads in the past, and with Async Ruby for the past 18 months.
From what I can tell, threads require a great deal of locks (mutexes) and are frustratingly hard to get right - because of the language-level race conditions.
Async Ruby has been refreshingly easy to write. I have yet to encounter an example of a race condition.
If it helps to bridge the language gap here: from what I know Async Ruby is similar to Go's goroutine model.
In fact this is a perfect use case for Falcon + Async.
Sorry if I wasn't explicit. I was talking about tasks/fibers performing actual work, like handling HTTP connections.
It's practically possible for Async Ruby programs to do work with hundreds of thousands Async Tasks (concurrent fibers).
Some users have worked with millions of Async tasks, but I'm not sure if it was practical work, or proof of concept.
> You can easily have tens of thousands of threads on Linux.
Thank you for the correction about threads on Linux. I'm not sure if you're talking about threads in general or threads in Ruby?
I've only lightly tested increasing the number of threads in Ruby to about a thousand, and my test code was very slow.
I think that has to do with thread switching overhead. Threads have a relatively high switching overhead, so I don't think it's advisable to run more than 100 threads - in Ruby at least.
It's trivially easy to get the results, here's a quick example:
require "async"
require "open-uri"
results = []
Async do |task|
task.async do
results << URI.open("https://httpbin.org/delay/1.6")
end
task.async do
results << URI.open("https://httpbin.org/delay/1.6")
end
endThere's a lot to say on this topic.
- Threads implement "preemptive scheduling". A scheduler switches control from one thread to another every 10ms or so. A thread running the code may be ready for the switch or not. The ensuing race conditions are nasty.
- Async + Fibers implement "cooperative scheduling". The currently running fiber (voluntarily) yields control to another fiber when it's ready. The result is there are no race conditions.
There's so much to say about this, I'll blog about this in the future.
Small correction: fibers (blocking or non-blocking) never used threads in any way. Fibers are "stackful coroutines", another concurrency primitive.
They're pretty hard to understand, and unfortunately I can't find a good explanation of "Ruby Fibers" that I can link to.
I think the gem did change ownership at some point. The first couple gem releases from 2013 were yanked.
Async gem is starting fibers automatically. Fiber pausing and resuming is handled by event reactor (also called "event loop") from 'nio4r' gem.
io_uring support with asynchronous File IO is also on the way.
Hm, the first commit to the gem was in 2017. The gem was not "advertised" so that the interfaces can be polished and done right.
Async gem is hugely improved with Ruby 3.0 release (from December 2020) when Ruby language added "fiber scheduler" feature just to integrate better with Async gem.
- Async is more scalable, can handle millions concurrent tasks (like HTTP connections). There can only be a couple thousand threads at the same time.
- Threads are really hard to work with - race conditions everywhere. Async doesn't have this.
It's obvious from the examples provided in the post. For example, you can use the method `URI.open` both synchronously and asynchronously.
It's something I didn't want to mention in the article because it's a relatively advanced async concept, but yea - Async Ruby is already colorless and it's great.
From what I was able to tell (I followed the discussion around this) enabling Rails to work with Async is not huge amount of effort. One guy was working on this, but he got distracted with other stuff.
It's just a matter of time someone steps up and gets this to work.
- Vim's commands and keybingings interface (not the UI).
- tmux
- mutt - email client
- wyrd + remind - calendar
- ledger - double-entry accounting tool
- weechat + weeslack plugin - fully featured, super fast slack from the CLI
My conclusion was: I don't know and don't have a specific mission. The closest I have to a mission is a set of positive (may probably not conscious) principles in life that I'm sticking to. Examples:
- always be improving - learn, learn, learn - work hard - presented with negative or ambiguous choices, choose the best available one (don't screw up everything because of frustration)
I'd also be curious to hear opinions on these:
- Is it "necessary" to have a specific higher mission? Can't I just stick to the set of positive principles? - Do I have to actively be looking for a mission? Or I wait and let it happen?
Maybe now that N26 is going to become a real bank supporting Apple Pay will become a possibility?
We use gRPC in my company. We're happy but some things were not easy or straightforward to implement. With this update nginx makes load balancing and authentication easier to implement.
And sure, this might not be the solution to humanity's overall mobility, but heck I can see it being useful in many ways.