线程和委托 - 使用类进行多线程处理

时间:2018-04-22 14:37:53

标签: python multithreading python-3.x delegates

我可以让下面的例子在不同的线程上运行(对于我正在调用的每个函数),但是当我尝试将它与类和委托结合起来时,我遇到了问题。

在下面的示例中,将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))

1 个答案:

答案 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()功能会调用您的computeprint_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)>

这个解释清楚了吗?如果您有任何疑问,请告诉我。希望这可以帮助! :)