Joshua Haberman wrote: > On Oct 3, 2005, at 9:21 PM, Tanaka Akira wrote: >> In article <3qctcuFecn8gU1 / individual.net>, >> "Robert Klemme" <bob.news / gmx.net> writes: >> >> >>> I have one question on this matter which I still don't understand >>> (I'm not >>> so deep into C stdlib IO variants so please bear with me): why >>> would anybody >>> want to use nonblocking IO (on the Ruby level, e.g. IO#read might >>> not have >>> read anything on return even if the stream is not closed) in the >>> light of >>> Ruby threads? I mean, with that one would have to build the >>> multiplexing in >>> Ruby which is already present in the interpreter with multiple >>> Ruby threads? >>> Are there situations that I'm not aware of where this is useful / >>> needed? >>> >> >> It is an interesting question I also have. >> >> I asked it several times, so I know some answers. >> >> 1. GUI framework has its own event driven framework. >> >> If a callback blocks, it blocks entire GUI. It is not >> acceptable. >> >> 2. High performance network server has its own event driven >> framework. >> >> Some high performance network servers use an application >> level event driven framework. If an event handler blocks, >> it blocks entire application. It is not acceptable. >> >> However I'm not sure that it is appropriate to implement >> a high performance server in Ruby. >> >> If an application level event driven framework is used, >> application level nonblocking I/O operations are required. >> >> If there are other usages, I'd like to know. > > Nonblocking I/O is useful if you are a server with some kind of > complex, global state, and lots of clients that can act on that > state. A good example would be a gaming server. If you handle every > client in its own thread, you need a big, coarse lock around your > global state. Once you're doing that, what's the point of > multithreading? It just makes things more complicated, and your > program's execution more difficult to understand. > > You might have many IO objects open that are interrelated. Say your > program logic is something like: > > when there's data available on object A, process it and send the > results to B and C > when there's data available on object B, process it and send the > results to A and C > when there's data available on object C, process it and send the > results to A and B > > How should I break this down into threads? Three threads that block- > on-read for A, B, and C? But what if A and B get data at the same > time? They might interleave their writes to C. Do I put a mutex > around C? > > For this case, it's a lot easier and more natural to write a main > loop like: > > while true > (read_ready, write_ready, err) = IO.select([A, B, C]) > read_ready.each { |io| > output = process(io.read) > [A, B, C].each { |client| client.write(output) unless client > == io } > } > end > > Nonblocking I/O gives you more control over the execution of your > program, and frees you from the worries of synchronizing between > threads. And it's simpler than using threads for programs that > follow certain patterns. Thanks for the feedback. Even in this case I'd probably choose a different architecture. I dunno which of these is easier but here's how I'd do it: Have a thread per open client connection that reads requests. Requests are put into a queue (thread safe!). Then I'd have a number of workers that fetch from the task queue and do the work. Either each worker sends results directly to affected clients or puts results into a second queue from which a number of sender threads fetch their tasks and send responses. There could also be dedicated sender threads per client. If there are no dedicated sender threads you would need just a single point of synchronization (apart from what queue does internally) for the sending socket in order to prevent multiple responses to interfere (if it's possible that a request is received while the answer to another request is being processed). Kind regards robert