• Advertisement

Archived

This topic is now archived and is closed to further replies.

Blocking + threads vs. non-blocking

This topic is 5099 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I know questions very close to this one have been asked (I searched already) but I''m new to this whole network coding thing and most of the discussions go right over my head. Basically, I see two alternatives: * Use blocking sockets and multithreading to prevent application lockup * Use non-blocking sockets and check for stuff periodically. Now I don''t really know the advantages to one or the other, but there are a fe things: * single processor systems are the target. SMP might be nice, but I''m not writing for it and I''m not going to try to take any real advantage on it. * I''ve never actually written multithreaded code that had to deal with thread syncing, mutexes, etc. and so I''ll have to learn all of that if I use blocking sockets. I need to know what the major advantages are to either method. I''m looking for maybe 64 simultaneous connections max, so the abilities of the socket shouldn''t be a problem in either case. Whatever I do has to be portable, but I''m using SDL so I can take advantage of SDL''s thread libraries. Any answers and explanation would be appreciated.

Share this post


Link to post
Share on other sites
Advertisement
quote:
Original post by Promit
Basically, I see two alternatives:
* Use blocking sockets and multithreading to prevent application lockup
* Use non-blocking sockets and check for stuff periodically.

* Use select.

Share this post


Link to post
Share on other sites
Doesn''t using select fall under the non-blocking category?

Or am I missing something here?

Share this post


Link to post
Share on other sites
select allows you to block on multiple nonblocking sockets, so you don''t have to poll and you only need one thread.


"Sneftel is correct, if rather vulgar." --Flarelocke

Share this post


Link to post
Share on other sites
quote:
Original post by Sneftel
select allows you to block on multiple nonblocking sockets, so you don't have to poll and you only need one thread.


"Sneftel is correct, if rather vulgar." --Flarelocke


Wait...

so I can call select and optionally have it block a nonblocking socket? So are you saying I should use nonblocking sockets and select?

I'm confused...what exactly does select do? And what do I do to make a socket blocking or nonblocking, anyway?

[EDIT] Currently reading MSDN docs on select.

[edited by - Promit on March 4, 2004 8:37:21 PM]

Share this post


Link to post
Share on other sites
You can use ioctlsocket() with the FIONBIO flag to enable/disable blocking mode on a socket.

Share this post


Link to post
Share on other sites
Actually, I kind of misspoke. Sockets by themselves are neither blocking nor nonblocking (when used with select). "select" can be used in blocking or nonblocking mode; in blocking mode, it blocks only until at least one socket in its set is ready to read/write. Thus, you can block on multiple sockets simultaneously.


"Sneftel is correct, if rather vulgar." --Flarelocke

[edited by - sneftel on March 4, 2004 8:51:35 PM]

Share this post


Link to post
Share on other sites
IIRC, with select blocking will depend on whether you set the fifth parameter (struct timeout) to 0 which means it will return right away regardless of state, greater than 0 is the amount of time (microseconds and milliseconds) it will wait for change in state before dropping out, and NULL will block until a file descriptor is ready.

Share this post


Link to post
Share on other sites
quote:
Original post by Nervo
IIRC, with select blocking will depend on whether you set the fifth parameter (struct timeout) to 0 which means it will return right away regardless of state, greater than 0 is the amount of time (microseconds and milliseconds) it will wait for change in state before dropping out, and NULL will block until a file descriptor is ready.


That doesn''t make sense; NULL and 0 are the same thing, they can''t behave differently...

Share this post


Link to post
Share on other sites
quote:
Original post by Promit
quote:
Original post by Nervo
IIRC, with select blocking will depend on whether you set the fifth parameter (struct timeout) to 0 which means it will return right away regardless of state, greater than 0 is the amount of time (microseconds and milliseconds) it will wait for change in state before dropping out, and NULL will block until a file descriptor is ready.


That doesn''t make sense; NULL and 0 are the same thing, they can''t behave differently...


Setting the members of the struct to zero will cause immediate drop-out. Setting the whole parameter to NULL will block. Sorry for the ambiguity.

Share this post


Link to post
Share on other sites
I think what he meant was if the contents of the timeval struct passed as the fifth paramter are equal to 0, then it will return immediately, and if instead of a valid timeval struct you pass a null pointer, then it will block.

Share this post


Link to post
Share on other sites
Ok, I think I get it.

But back to the original question, whare are the pros/cons of the various methods?

Share this post


Link to post
Share on other sites
int select(int numfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);

struct timeval {
int tv_sec; // seconds
int tv_usec; // microseconds
};


btw, select is beautiful because its implemented the same way in winsock as in BSD sockets. good for cross platform compatibility, IMHO.

Share this post


Link to post
Share on other sites
One quick Q about select:

According to MSDN, the numfds parameter is ignored and only included for compatibility with BSD sockets. This suggests that numfds does something on BSD/nixes. What does it do?

Share this post


Link to post
Share on other sites
It specifies the max number of the file descriptors that have been placed in the fdsets.

edit: every time I write this it seems to come out wrong. Every file descriptor is an integer value. nfds should be the max of the integral value of all the file descriptors that you care about.

[edited by - SiCrane on March 4, 2004 9:34:46 PM]

Share this post


Link to post
Share on other sites
Yeah, select() is wonderful for cross compatibility. My Socket Class compiles under windows and linux. If i were you i''d stay well away from async sockets, they''re evil evil evil.

I use select() with blocking sockets in my MUD server, and it works quite happily. I suppose it could be done better if i select()ed on more that one socket at a time though...

Share this post


Link to post
Share on other sites
select() on all your sockets at the same time. No exceptions. Let select() block. If your system needs periodic idle time or running events, then select() with a timeout until the next event is due.

There really isn''t much reason to use anything else. This is portable and reasonably high performance. (For those last few percent, you need platform specific calls, like /dev/poll or Async Winsock.)

Multi-threading just causes more synchronization, which ends up costing performance, in my experience.

Share this post


Link to post
Share on other sites
So could I let select block in a seperate thread? I''m rendering at the same time, so I really can''t afford even to let it just time out and then continue...

And if select is blocking, then I''ll need another thread when I actually need to send something to the clients, right?

Share this post


Link to post
Share on other sites
Don''t put select in another thread. Threads add synchronization overhead.

Call select, and pass it a timeval pointer that points at a 0,0 timeval, for an "immediate" timeout. That way, select polls the sockets and will tell you which ones actually have data available or are ready to wrote; then you go and read/write those sockets.

forever {
read user input
service the network
run physics
render one frame
}

Although, to keep in sync with the other end, your physics (and possibly networking and user interface) will probably need to run at a fixed frame rate.

Share this post


Link to post
Share on other sites
quote:
Original post by hplus0603
Don''t put select in another thread. Threads add synchronization overhead.


Given the number of processes and threads already running on a typical machine, having 2 or 3 threads in a game isn''t going to hurt.

A quick test shows that ROTW - Ennemy Territory uses at least 7 threads.


“Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.”
— Brian W. Kernighan (C programming language co-inventor)

Share this post


Link to post
Share on other sites
Yes, most games will use threads for several common things:

1) sound -- DirectSound will start some for you, if nothing else
2) non-blocking I/O -- because overlapped I/O does not exist on Win9x/ME, you have to emulate it with threads
3) non-blocking DNS resolution -- gethostbyname() is blocking, you usually want to make it async with a thread

However, in all these cases, the thread is NOT involved in step-by-step operations within the game. If you involve more than one thread for each step or frame of your game, then you need to pay synchronization overhead, which is 1-2 microseconds for an UNCONTENDED critical section (because of the PCI bus and the x86 memory model, so it doesn''t improve with hardware), and 10-20 microseconds for a blocking kernel primitive (depending on OS and CPU speed).

Maybe you think it makes sense to have one thread which reads from a socket, and posts messages into an incoming queue (while locking the queue), and then have the game lock the queue and pull data out each step. However, this doesn''t really buy you anything above just using select() or non-blocking sockets, and it costs you the interlock. Also, if you have a shared lock, then one thread (say, the network read) may be pre-empted with the lock held, and the other thread will wait until the network thread can be scheduled again and release the lock.

Threads really aren''t the end-all of everything; you should only use them when you have to, for performance AND correctness reasons.

Share this post


Link to post
Share on other sites

  • Advertisement