05-Aug-2013 00:59, Marek Janukowicz пишет:
Dmitry Olshansky wrote:
There are more things specific to this particular application that would
play a role here. One is that such "real workers" would operate on a common
data structure and I would have to introduce some synchronization. Single
worker thread was not my first approach, but after some woes with other
solutions I decided to take it, because the problem is really not in
processing (where a single thread does just fine so far), but in socket
read/write operations.
Then what will make it simple is the following scenario
X Input threads feed 1 worker thread by putting requests into one shared
queue.
You would have to use lock around it or get some decent concurrent queue
code (but better start with simple lock + queue)...
Got carried away ... you can just easily use std.concurrency message
passing (as *it is* an implicit message queue).
Then just throw in another writer thread that recieves pairs of
responses + sockets (or shared void* e-hm) from "real worker".
The pipeline is then roughly:
Acceptor
--CREATES--> InputWorkers (xN)
--SEND REQ--> Real Worker
--SOCK/RESP--> Writer
2. Create separate thread per each client connection. I think this could
result in a nice, clean setup, but I see some problems:
- I'm not sure how ~50 threads will do resource-wise (although they will
probably be mostly waiting on Socket.select)
50 threads is not that big a problem. Around 100+ could be, 1000+ is a
killer.
Thanks for those numbers, it's great to know at least the ranges here.
The benefit with thread per client is that you don't even need
Socket.select, just use blocking I/O and do the work per each parsed
request in the same thread.
Not really. This is something that Go (the language I also originally
considered for the project) has solved in much better way - you can "select"
on a number of "channels" and have both I/O and message passing covered by
those.
They multiplex stuff in their runtime. In fact AFAIK they don't even
have clean-cut native threads. It would be interesting to see how they
handle it but I guess either self-pipe or event-driven + async I/O to
begin with.
In D I must react both to network data or message from worker
incoming, which means either self-pipe trick (which leads to Socket.select
again) or some quirky stuff with timeouts on socket read and message receive
(but this is basically a busy loop).
Sadly like others said with std.socket you get to witness the gory glory
of BSD sockets API that shows its age. Regardless it's what all major OS
directly provide.
Btw.
would it work if I pass a socket to 2 threads - reader and writer (by
working I mean - not running into race conditions and other scary concurrent
stuff)?
Should be just fine.
See also
http://stackoverflow.com/questions/1981372/are-parallel-calls-to-send-recv-on-the-same-socket-valid
Also I'm really puzzled by the fact this common idiom doesn't work in some
elegant way in D. I tried to Google a solution, but only found some weird
tricks. Can anyone really experienced in D tell me why there is no nice
solution for this (or correct me if I'm mistaken)?
The trick is that Socket/std.socket was designed way back before
std.concurrency. It's a class as everything back then liked to be.
The catch is that classes by default are mutable and thread-local and
thus can't be automatically _safely_ transfered across threads.
There were/are talks about adding some kind of Unique helper to
facilitate such move in a clean way. So at the moment - nope.
--
Dmitry Olshansky