当只有一个线程时,为什么我们需要使用线程锁? (蟒蛇)

时间:2018-04-19 12:25:38

标签: python multithreading

我不明白为什么只有一个线程时会使用线程锁定。这是我在网上看到的代码 是不是只有当主线程以外的线程超过2个时才使用线程锁?

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()

我也不明白为什么在这一行中使用了线程锁

    tLock.acquire()
    message = raw_input(alias + "-> ")
    tLock.release()

如果未使用线程锁定会发生什么?

4 个答案:

答案 0 :(得分:2)

这里有一行:

tLock.acquire()
message = raw_input(alias + "-> ")

在提示用户向服务器发送查询/请求之前,有效地等待来自服务器的回复消息

答案 1 :(得分:2)

据推测,背后的想法是接收器线程在您输入自己的消息时不会在控制台上转储新的(已接收)消息。根据环境的不同,当您在控制台中输入内容并从某些后台活动中显示新内容时,这可能会非常烦人。

它在接收器线程上运行正常:它获取锁定,如果有可用数据,它会连续打印,即使它长于缓冲区大小(1024)。当数据耗尽(或者没有)时,recvfrom“失败”(请记住它是一个非阻塞套接字,这就是setblocking(0)调用的作用,尽管它的参数应该是{ {1}}),因此锁在False - 块中释放,外循环继续。这是输入线程(主要线程)可以接管的点。

主线程有一个更值得怀疑的设计:当用户输入自己的消息(锁定被锁定时发生finally调用)时,实现了直接目标并且没有传入消息扭曲控制台,它也意味着只有在raw_input()完成后才会显示任何进一步的传入消息。这就是为什么不发送空消息的原因:如果您想查看可能的新传入消息,则必须不时按Enter键。

答案 2 :(得分:2)

您的程序设计可能基于对锁是什么的错误理解。如果你期望线程B在线程A释放它时等待获取锁定而被唤醒,那么你可能会犯错误。这不是它的工作方式。

锁定 可以有不同的方法,但除非您使用的库的文档承诺超过最低限度(并且,Python的文档{{1} <}模块没有做出那个承诺),那么你应该只假设最低限度;

  • 如果主题A调用Threading,当lck.acquire()可用时,则该调用将立即成功。

  • 如果线程A在lck调用中阻止了一个或多个其他线程时调用lck.release(),那么稍后,至少有一个线程将被允许​​重新尝试获取lck.acquire()

    • 如果重试成功,则lck来电将返回
    • 否则线程将返回等待。

这是lck.acquire()主题的简化版本:

receive

让我们说,当消息到达def receiving(name, sock): while not shutdown: tLock.acquire(); data = sock.recvfrom(...); doSomethingWith(data); tLock.release(); 时,主线程正在tLock.acquire()中等待。以下是最有可能发生的事情:

sock

在主线程中没有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. 调用返回的情况下,该进程可以重复多次。

此问题是 resource starvation 的一个示例。

答案 3 :(得分:1)

你这里有两个线程。 receiving线程开始于此:

rT = threading.Thread(target=receving, args=("RecvThread",s))
rT.start()

会发生什么?receiving线程将首先获取锁,并尝试在从主线程传入的套接字上接收。同时,主线程会提示用户输入消息,然后将其发送到同一个套接字上。该消息由receiving线程接收,然后释放锁。然后主线程获取锁定并提示输入新消息,然后我们进入获取和释放两个线程之间锁定的循环。因此,在主线程中获取锁的目的只是等待receiving线程在从主线程发送新消息之前接收消息。