我可以让下面的例子在不同的线程上运行(对于我正在调用的每个函数),但是当我尝试将它与类和委托结合起来时,我遇到了问题。
在下面的示例中,将threading.current_thread()
调用到类中,我总是得到相同的线程。
为什么会发生这种情况?
import threading
import time
class FirstClass(threading.Thread):
def __init__(self, obj):
threading.Thread.__init__(self)
self.obj = obj
print(threading.current_thread())
def compute(self, num_list):
for n in num_list:
time.sleep(0.2)
print("Square: {}".format(n*n))
self.obj.print_it(n)
class SecondClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
print(threading.current_thread())
def print_it(self, n):
time.sleep(0.2)
print("Cube: {}".format(n*n*n))
t = time.time()
t1 = SecondClass()
t2 = FirstClass(t1)
t2.compute([1,2,3,4,5])
t1.start()
t2.start()
t1.join()
t2.join()
print("Done in: {}".format(time.time()-t))
答案 0 :(得分:2)
好的,在我尝试了很长一段时间后得到的解决方案之前几点:(如果你想跳过解释,工作代码如下)
(i)如果你看看Thread.start()做了什么,你会看到提到它
安排在单独的控制线程中调用
run()
方法。
那么,这意味着,目前,在线程类的代码中没有run()
函数,它不会在单独的控制线程中调用其他函数。
因此主线程正在执行所有操作。要理解这一点,请在每个线程类中放置两个运行函数定义:
def run(self):
print(threading.current_thread())
并尝试运行它。您现在将看到,调用了单独的线程。
(ii)那么您如何从run()
调用自定义函数?
当您查看Thread.run()的定义时,它会说
标准run()方法调用传递给对象构造函数的可调用对象作为目标参数(如果有),分别从args和kwargs参数中获取顺序和关键字参数。
所以,一种方法是在调用t1 = SecondClass(args = [1,2,3,4,5])
中传递您的参数,然后在__init__
中接收并初始化它:
def __init__(self, args):
您的run()
功能会调用您的compute
或print_it
功能
def run(self):
print_it(self, self.args)
(iii)但是现在,你如何在循环中每次调用另一个线程?你只能启动一个一次的线程,如果你每次尝试为循环中的self.object.run
线程提供t1
,那么不会去回到线程的执行。它将在同一个线程t2
中继续该函数。
(iv)此外,您如何将当前列表值传递给t1
,执行多维数据集根,然后跳回t2
并继续下一个数字的平方?
对于求解(iii)和(iv),你需要一个Queue
(共享列表可以驻留的位置)和一个可以安全地执行一个线程的同步机制,然后控制下一个线程,再回来等等。
请记住,线程共享相同的地址空间,因此我们应该小心避免竞争条件,并且一次只有一个线程可以使用调度程序。这就是需要同步机制的原因。
好的,所以有更好的方法来解决你的问题(比如只有两个函数用于square和cube并创建两个线程直接调用这些函数而不涉及每个线程的类)但是我已经找到了一个解决方案你可以用来达到理想的效果。
以下是代码(注释为解释):
import threading, queue # need a queue for storing the shared values between threads
import time
class FirstClass(threading.Thread):
def __init__(self, obj, args): # pass the extra args here
threading.Thread.__init__(self)
self.obj = obj
self.args = args # initialize the args
def run(self):
compute(self, self.args)
def compute(self, args):
num_list = args[0]
lock = args[2] # lock object was passed in args[2]
for n in num_list:
lock.acquire_for('A') # acquire the lock
time.sleep(0.1)
print("Square: {}".format(n*n))
print(threading.current_thread())
args[1].put(n) # args[1] is the queue object, put number in queue
lock.release_to('B') # release the lock to waiting thread
class SecondClass(threading.Thread):
def __init__(self, args):
threading.Thread.__init__(self)
self.args = args
def run(self):
print_it(self, self.args)
def print_it(self, args):
lock = args[2]
num_list = args[0]
for a in num_list: # this is to iterate the exact number of times t2 iterates,
# so that the lock is released only by the thread that acquired it
# here we are alternating, so without this, the execution would stop
lock.acquire_for('B') # acquire when t2 releases lock
n = args[1].get() # args[1] is the queue object, get from queue
time.sleep(0.5)
print("Cube: {}".format(n*n*n))
print(threading.current_thread())
lock.release_to('A') # give back lock
class LockWithOwner:
lock = threading.RLock()
owner = 'A'
def acquire_for(self, owner):
n = 0
while True:
self.lock.acquire()
if self.owner == owner:
break
n += 1
self.lock.release()
time.sleep(0.001)
print('Waited for {} to be the owner {} times'.format(owner, n))
def release_to(self, new_owner):
self.owner = new_owner
self.lock.release()
if __name__ == '__main__':
q = queue.Queue()
t = time.time()
lock = LockWithOwner()
lock.owner = 'A'
args_list = [1,2,3,4,5]
t1 = SecondClass(args = (args_list, q, lock)) # pass the number list, queue & lock object
t2 = FirstClass(t1, args = (args_list, q, lock))
t1.start()
t2.start()
t1.join()
t2.join()
print("Done in: {}".format(time.time()-t))
详细了解此处使用的锁:threading.RLock
因此,锁定类的作用是,最初线程t1只能在所有者设置为A时启动作业,而线程t2只能在所有者设置为B时启动作业。一旦线程调用{{1它获取对调度程序的锁定,执行,并在线程调用acquire_for
时,它将所有者分配给传递的参数(另一个线程)并释放锁。
运行上述代码时的输出将是:
release_to
Waited for A to be the owner 0 times
Square: 1
<FirstClass(Thread-2, started 12360)>
Waited for B to be the owner 1 times
Cube: 1
<SecondClass(Thread-1, started 204)>
Waited for A to be the owner 1 times
Square: 4
<FirstClass(Thread-2, started 12360)>
Waited for B to be the owner 1 times
Cube: 8
<SecondClass(Thread-1, started 204)>
Waited for A to be the owner 1 times
Square: 9
<FirstClass(Thread-2, started 12360)>
Waited for B to be the owner 1 times
Cube: 27
<SecondClass(Thread-1, started 204)>
Waited for A to be the owner 1 times
Square: 16
<FirstClass(Thread-2, started 12360)>
Waited for B to be the owner 0 times
Cube: 64
<SecondClass(Thread-1, started 204)>
Waited for A to be the owner 1 times
Square: 25
<FirstClass(Thread-2, started 12360)>
Waited for B to be the owner 1 times
Cube: 125
<SecondClass(Thread-1, started 204)>
这个解释清楚了吗?如果您有任何疑问,请告诉我。希望这可以帮助! :)