Jump to content
  • Advertisement
Sign in to follow this  
ninmonkeys

multi-threading: basics

This topic is 3827 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

Using ref: http://heather.cs.ucdavis.edu/~matloff/Python/PyThreads.pdf I'm trying to learn multi-threading. I tried to understand threading by writing a simple portscanner. ( uses 'ping' on each CPU on the LAN ) (1) Is there a max_limit to how many threads can be alive at once? Because if I scan 50 IP's its safe. But range: 1-80 and it makes a lot of dings. ( seems to be on calls to system("ping"), but not every call. seems random ) Then I tried 1-255, and it still dings. And sometimes ( not always ) it makes 1 or more ( sometimes alot ) of dialogs, which say: "application falied to initialize properly(0xc0000142) Click on OK to terminate the application." (2) if there is a limit, how do I wait untill a slot is free in thread_list, and use it? (3) The output text scrolls fast and fills the limit of cmd.exe's buffer, so I can't find the exact text. I think sometimes the code says something like "unhandled exception " ( related to threads ) but it doesn't end the program like a normal unhandled exception does! (4) How do you know if an operation requires locking? I would have thought 'a += b' would be safe, but the tutorial says its not. If assignment isn't safe, is anything safe? ( only operations that are read only? ) (5) Sometimes garbage in STDOUT. ( maybe related to #1,#2, or #3 above ) Garbage below shown as '?'.
Pinging 192.168.255.7 with 32 bytes of data:
Pinging 192.168.255.8 with 32 bytes of data:
???Pinging 192.168.255.9 with 32 bytes of data:??????
Pinging 192.168.255.10 with 32 bytes of data:
Pinging 192.168.255.11 with 32 bytes of data:

Here's my code:
import os,sys
import thread
import threading
import time
print """----------------------
attempt at: multi-threaded basic portscanner. ( not really portscanner, just
does a ping to see what my LAN cpu's IP's are. )

Seems to be working, except for (1)strange ding,(2) possible unhanlded exception
and (3)dialog error :(

usage:
	
"""
class Log():
	def __init__(self, file):
		self.log_lock = thread.allocate_lock()
		self.log = open(file, "a")
		self.write("== %s/%s/%s %s:%s:%s ==\n" % time.localtime()[0:6])
	def write(self, str):
		"""thread safe writing ( i *think* ).
		not sure if in this case ( file appending) if locking is required?"""
		self.log_lock.acquire()	
		self.log.write("%s\n" % str)
		self.log_lock.release()

class ScanAddr( threading.Thread ):    
	def __init__( self, ip,cur,log ):
		self.ip = ip
		self.cur = cur
		self.log = log
		threading.Thread.__init__(self)
		
	def run( self ):
		res = os.system("ping %s.%s -n 1" % (self.ip, self.cur ))
		# lock and log
		if res == 0:
			print "%s.%s is online" % (self.ip, self.cur)
			self.log.write("%s.%s is online" % (self.ip, self.cur) )
		elif res == 1:
			print "%s.%s is offline, or ping request blocked." % (self.ip, self.cur)
	
ip = sys.argv[1]
start = int(sys.argv[2])
end = int(sys.argv[3])
cur = start

# init: locks:
cur_lock = thread.allocate_lock()

# init: log 
log = Log("05.log.txt")
log.write("scanning: %s.%s-%s" % (ip, start, end ))

thread_list = [] # holds instances of ScanAddr(...), never gets cleared!
while cur<end:
	# do scan:
	cur_thread = ScanAddr( ip,cur,log )
	cur_thread.start()
	thread_list.append( cur_thread )
		
	# don't think know if I actually need to lock cur inc. or not. 
	# Tut says "I do"
	cur_lock.acquire()
	cur += 1
	cur_lock.release()


Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by ninmonkeys
(1) Is there a max_limit to how many threads can be alive at once?

Generally speaking, not a well-defined one. I don't know if Python specifically has a thread count limit, but I doubt it.

Usually, you can create threads until you run out of memory. (Each thread has its own stack, so it'll allocate a chunk of memory. Which means there's only room for so many threads)

However, there can be plenty of other limitations. I've no clue what you mean by "dings", but when calling external programs, like ping, which uses network sockets, you can probably hit a lot of internal limitations.

[quot€](3) The output text scrolls fast and fills the limit of cmd.exe's buffer, so I can't find the exact text. I think sometimes the code says something like "unhandled exception " ( related to threads ) but it doesn't end the program like a normal unhandled exception does!
[/quote]
Is there a question? [wink]

Quote:
(4) How do you know if an operation requires locking? I would have thought 'a += b' would be safe, but the tutorial says its not. If assignment isn't safe, is anything safe? ( only operations that are read only? )

*anything* that is accessed by more than one thread, where at least one thread is going to write data. Anything, no matter how small. As you say, even simple assignment aren't safe.

As long as *all* threads perform read only operations though, you're safe.

Share this post


Link to post
Share on other sites
Quote:
Original post by SpoonbenderHowever, there can be plenty of other limitations. I've no clue what you mean by "dings", but when calling external programs, like ping, which uses network sockets, you can probably hit a lot of internal limitations.
The 'ding's are the default win32 error sounds.

[1] Do you have any guidelines on how should I define the limits? Or is it trial-and-error?

Quote:
Original post by Spoonbender
Quote:
(3) The output text scrolls fast and fills the limit of cmd.exe's buffer, so I can't find the exact text. I think sometimes the code says something like "unhandled exception " ( related to threads ) but it doesn't end the program like a normal unhandled exception does!

Is there a question? [wink]
[2] I saw an error relating to either module 'thread' or 'threading', saying something about an unhandled exception. Do you have any idea what it was? I can't seem to reproduce it.

[3] based on the docs, I think these are equivalent ways to lock. Am I right?


# [A]
some_lock = thread.allocate_lock()
some_lock.acquire()
x += 1
# ...
some_lock.release()

#
some_rlock = threading.RLock()
with some_rlock:
x += 1
# ...



Since I want to try using a list of threads and a max number: max_threads value. And if max number is reached: don't spawn new thread. Instead wait until an old thead is done:

[4]is the module Queue the right tool for this?
--
thanks,
monkey

Share this post


Link to post
Share on other sites
[1] Thread limits:
Well the thread limit depends on your OS-configuration. For windows it should be a few thousand. But using so many threads is not a good idea. Once I worked a small project using a few thousand threads to handle incoming requests and it was a major preformance disaster. The reason is, that the OS produced a major overhead by handling so many threads.
You only need few threads to handle many thousand request.

Basic architecture:
receiver(1x) -> dispatcher(1x) -> workers(10x)

Description:
You got one or more receiver who take incoming messages and adds them to a queue. On the other hand you got a pool of worker, lets say 10, and 1 dispatcher. Whenever a message arrived in the queue, the dispatcher checks for a free worker. If there's atleast one free worker available, the dispatcher unqueue the message, transfer it to the worker and starts the worker. This approach is extremly efficient.

Problems:
This works only if you don't use a connection bound communication over a long time period (i.e. hold a tcp connection during complete game session). In this case I would suggest to either build up the connection only during the request (application/webserver often do this),which might be not the best solution for a game, or to use a connectionless protocol (based on udp).

[2] none reproducable exceptions:
This is in 95% a matter of accessing shared resources. Multithreading and shared resources is always a delicate issue. Try to identify all shared resources and secure them by using mutex/semaphore mechanism.

--
Ashaman

Share this post


Link to post
Share on other sites
Quote:
Original post by Ashaman73
The exception occured cause thread 2 thinks that gMonster is not null, while thread 1 set it to null just before thread 2 is able to continue its work.

:)

--
Ashaman


What? He said, if *all* your threads are only reading. In your example you're setting the pointer to NULL, hence you're not only reading. You're changing the memory block which the pointer is pointing at.

Share this post


Link to post
Share on other sites
I haven't used python, but I have used C++ threads on Win32 quite a bit, so take this with a pinch of salt.

1: As said the limit is OS defined, but commonly is limited to the amount of free or available memory you have. I've found that you generally want to keep this number at or around the number of cores you have available, give a few more for Async I/O and networking. Your using an external program to do the majority of your work it seems, so there is no guarantee that the ping program is actually set up, or can handle this.

2: One way is to set up a work queue system where each ping request is sent to a FIFO list or a queue structure where you have one (master thread) and several worker threads. The master thread allocates work to the workers, and keeps updated of their progress. Once a request is completed, the worker signals and it's executable code is replaced (function pointers in c/c++)

3: If a thread encounters an unhanded exception, what it should do is very much language and system dependent. Default behavior in C++ is to call Abort(). However, a spawned thread can abort itself but may not be under any obligation to terminate its parent or siblings, in some cases doing that would be highly dangerous.

4: The most blanket way I can think to say if something needs looking is that if an item is not atomic, it is written to or there is a chance that part way through an expression the thread may be interrupted and a thread from the same process scheduled that may read or write to the same location, it should be locked.

In your example: a += b defiantly needs locking.

Most people think of something like a += b (as its a single expression) as a single instruction. This isn't true. a += b can be thought of as shorthand for the (pseudo) instructions

get A -> get B -> add B to A -> assign result to A

Say you have one thread that executes this expression and gets to the last part but a context switch occurs and the result is not yet saved to A. Now a second thread from the same process comes and changes B and then uses A, at which point this still contains the old value. It then switches back to the first thread. The value of B now previously used in the expression is now different but it used the old value. Now when thread 1 saves the result of A it will be using the old values for A and B, whereas thread 2 thinks B and A are something differnet.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!