Reputation: 140
i don't understand why thread lock is used when there is only one thread. here is the code i've seen online isn't thread lock used only when there's more than 2 threads excluding main thread?
import socket
import threading
tLock = threading.Lock()
shutdown = False
def receving(name, sock):
while not shutdown:
try:
tLock.acquire()
while True:
data, addr = sock.recvfrom(1024)
print str(data)
except:
pass
finally:
tLock.release()
host = '127.0.0.1'
port = 0
server = ('127.0.0.1',5000)
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
s.bind((host, port))
s.setblocking(0)
rT = threading.Thread(target=receving, args=("RecvThread",s))
rT.start()
alias = raw_input("Name: ")
message = raw_input(alias + "-> ")
while message != 'q':
if message != '':
s.sendto(alias + ": " + message, server)
tLock.acquire()
message = raw_input(alias + "-> ")
tLock.release()
time.sleep(0.2)
shudown = True
rT.join()
s.close()
also i do not understand why thread lock was used in this line
tLock.acquire()
message = raw_input(alias + "-> ")
tLock.release()
what's going to happen if the thread lock was not used?
Upvotes: 3
Views: 1126
Reputation: 27210
The design of your program may be based on a flawed understanding of what locks are. If you expect that thread B will wake up from waiting to acquire a lock when thread A releases it, then you may be making a mistake. That's not quite how it works.
There are different ways that locking can work, but unless the documentation for the library that you are using promises more than the bare minimum (and, the doc for Python's Threading
module does not make that promise), then the bare minimum is all you should only assume;
If thread A calls lck.acquire()
, when lck
is available, then the call will immediately succeed.
If thread A calls lck.release()
while one or more other threads is blocked in an lck.acquire()
call, then some time later, at least one of those threads will be allowed to re-try acquiring lck
.
lck.acquire()
call will return,Here's a simplified version of your receive
thread:
def receiving(name, sock):
while not shutdown:
tLock.acquire();
data = sock.recvfrom(...);
doSomethingWith(data);
tLock.release();
Let's say, that the main thread is waiting in tLock.acquire()
when a message arrives on sock
. Here's what most likely happens:
receive thread
-------------------
receive the message
doSomethingWith(data)
tLock.release()
- set status of tLock to "available"
- Sees main thread is waiting for tLock
- Changes state of main thread to RUNNABLE
- returns.
tLock.acquire()
- Sees that tLock is "available",
- Changes it to "locked" and returns.
sock.recvfrom(...)
- no message is immediately available,
- goes into a wait state.
system heartbeat interrupt handler
----------------------------------
triggered by hardware
Sees that main thread is RUNNABLE
Kicks some other RUNNING thread off its CPU, and
allows the main thread to start running on that CPU.
main thread
-----------
...still in a call to tLock.acquire()
- sees that tLock still is "locked",
- goes back to waiting.
That process can repeat itself many times without the tLock.acquire()
call ever returning in the main thread.
This problem is an example of resource starvation.
Upvotes: 2
Reputation: 13225
Presumably the idea behind is that the receiver thread will not dump new (received) messages on the console while you are typing your own message. Depending on the environment it can be really annoying when you are typing something into a console and new things appear from some background activity.
It works okay-ish on the receiver thread: it acquires the lock, and if there is data available, it gets printed continuously, even if it is longer than the buffer size (1024). When data runs out (or there were not any), recvfrom
"fails" (remember that it is a non-blocking socket, that is what the setblocking(0)
call does, though its argument should rather be False
), so the lock is released in the finally
-block and the outer loop continues. This is the point where the input-thread (the main one) can take over.
The main thread has a more questionable design: while the immediate goal is achieved and no incoming messages distort the console while the user is typing their own message (the raw_input()
call happens when the lock is locked), it also means that any further incoming messages will appear only after raw_input()
completes. This is why empty messages are not sent: you have to press Enter from time to time if you want to see possible new incoming messages.
Upvotes: 2
Reputation: 11020
You effectively have two threads here. The receiving
thread is kicked off with this:
rT = threading.Thread(target=receving, args=("RecvThread",s))
rT.start()
What happens is that the receiving
thread will acquire the lock first and attempt to receive on the socket passed in from the main thread. The main thread in the meantime prompts the user for a message and then sends it on the same socket. The message is received by the receiving
thread which then releases the lock. The main thread then acquires the lock and prompts for the new message and then we get into the loop of acquiring and releasing the lock between the two threads. So the purpose of acquiring the lock in the main thread is simply to wait for the receiving
thread to receive the message before sending a new one from the main thread.
Upvotes: 1
Reputation: 39414
The lines here:
tLock.acquire()
message = raw_input(alias + "-> ")
effectively wait for a reply message from the server before prompting for the user to send a query/request up to the server
Upvotes: 2