使用with语句和Python线程技术

6
我是一个尝试理解Python线程模块的菜鸟。我正在使用Python 2.7版本。其中一个使用with语句的动机是代码模式。
with threading.Lock():
        //User defined function in a new thread

我不确定我是否正确理解了它,但我的初步假设是这段代码应该在主线程上获取锁,一旦子线程完成后就会释放该锁。然而,这个脚本

from __future__ import print_function
import threading
import time
import functools
#import contextlib
#Thread module for dealing with lower level thread operations.Thread is limited use Threading instead.

def timeit(fn):
    '''Timeit function like this doesnot work with the thread calls'''
    def wrapper(*args,**kwargs):
        start = time.time()
        fn(*args,**kwargs)
        end = time.time()
        threadID = ""
        print ("Duration for func %s :%d\n"%(fn.__name__ +"_"+ threading.current_thread().name ,end-start))
    return wrapper

exitFlag = 0

@timeit
def print_time(counter,delay):
    while counter:
        if exitFlag:
            thread.exit()
        time.sleep(delay)
        print("%s : %s_%d"%(threading.current_thread().name,time.ctime(time.time()),counter))
        counter -= 1

class Mythread(threading.Thread):
    def __init__(self,threadID,name,counter,delay):
        threading.Thread.__init__(self)
        self.threadID = threadID
        self.name = name
        self.counter = counter
        self.delay = delay

    def run(self):
        print("Starting%s\n" % self.name)
        print_time(self.counter, self.delay)
        print("Exiting%s\n" % self.name)


if __name__ == '__main__':
    '''
    print_time(5, 1)
     threadLock = threading.Lock()
     threads = []
     thread1 = Mythread(1,"Thread1",5,1)
     thread2 = Mythread(2,"Thread2",5,2)
     thread1.start()
     thread2.start()
     threads.append(thread1)
     threads.append(thread2)
     for t in threads:
         t.join()
    '''
    thread1 = Mythread(1,"Thread1",5,1)
    thread2 = Mythread(2,"Thread2",5,2)
    lock = threading.Lock()
    with lock:
        thread1.start()
        thread2.start()

    print("Exiting main thread ")

产生以下输出:
StartingThread1

StartingThread2

Exiting main thread 
Thread1 : Sat May 04 02:21:54 2013_5
Thread1 : Sat May 04 02:21:55 2013_4
Thread2 : Sat May 04 02:21:55 2013_5
Thread1 : Sat May 04 02:21:56 2013_3
Thread1 : Sat May 04 02:21:57 2013_2
Thread2 : Sat May 04 02:21:57 2013_4
Thread1 : Sat May 04 02:21:58 2013_1
Duration for func print_time_Thread1 :5

ExitingThread1

Thread2 : Sat May 04 02:21:59 2013_3
Thread2 : Sat May 04 02:22:01 2013_2
Thread2 : Sat May 04 02:22:03 2013_1
Duration for func print_time_Thread2 :10

ExitingThread2

请帮助我理解为什么使用with语句时锁定不起作用,或者我完全误解了这个概念。即使定义了锁,我也不明白为什么会直接进入print("Exiting main thread")。

通常情况下,创建一个不与任何其他线程共享的锁是没有意义的。你希望这个锁在这里保护你免受什么? - abarnert
我在这里尝试的是在以下代码之后执行此语句 print("Exiting main thread ") 代码如下: with lock: thread1.start() thread2.start() 当我显式调用以下代码时,它可以正常工作 thread1.join() thread2.join() 但我更喜欢使用with_statement,因为它看起来更干净。 - Rahuketu86
顺便提一下:您没有在线程上调用join,也没有将它们设置为daemon=True。这是非法的。在某些情况下它可以工作(但在不同的平台上会有不同的结果!),但您永远不应该依赖它。 - abarnert
我不明白你所说的“我更喜欢使用with_statement,因为它看起来更干净”的意思。它做的完全是另一回事,所以它会产生完全不同的效果。Lock甚至不是与Thread相同类型的对象。这就像说“我写map(print, [1,2,3])而不是2+4,因为前者看起来更干净”。 - abarnert
1个回答

13
您现有的基本上什么也不做。没有其他线程引用它,因此它不能在任何地方阻止其他线程的执行。它唯一可能做的事情就是浪费几微秒的时间。因此,下面这段代码:
lock = threading.Lock()
with lock:
    thread1.start()
    thread2.start()

...可以说与以下内容等价:

time.sleep(0.001)
thread1.start()
thread2.start()

我很确定这不是你想要的。

如果你想强制线程按顺序运行,最简单的方法就是不使用线程。

或者,如果你必须使用线程,在启动下一个线程之前等待上一个线程完成:

thread1 = Mythread(1,"Thread1",5,1)
thread2 = Mythread(2,"Thread2",5,2)
thread1.start()
thread1.join()
thread2.start()
thread2.join()

如果您希望使线程自我序列化,而无需外部帮助,则需要为它们提供一个可以共享的锁。例如:

class Mythread(threading.Thread):
    def __init__(self,threadID,name,counter,delay,lock):
        threading.Thread.__init__(self)
        self.lock = lock
        # ...
    def run(self):
        with self.lock:
            # ...

现在,要调用它们:
lock = threading.Lock()
thread1 = Mythread(1,"Thread1",5,1, lock)
thread2 = Mythread(2,"Thread2",5,2, lock)
thread1.start()
thread2.start()
# ...
thread1.join()
thread2.join()

现在,每个线程启动时,它将尝试获取锁。其中一个线程将成功获取锁,另一个线程将阻塞,直到第一个线程通过退出其with语句释放锁。
如果您不想对线程进行串行化处理,只是希望主线程等待所有其他线程完成...那么您所需要的就是join。这正是join所用之处,没有必要添加其他内容。
如果你真的想这样做,你可以将线程设为守护进程并等待同步对象。我无法想出使用锁定进行此操作的简单方法,但使用BoundedSemaphoreCondition应该很容易(尽管您必须两次等待条件)。但这是一件非常愚蠢的事情,所以我不确定您为什么要这样做。

在哪里可以进行以下操作: 前置条件: 使用锁: // f(io_peration1) // f(io_operation2) f(处理生成的输出文件) - Rahuketu86
我的代码反复遇到这种模式,在每个任务中都要这样做:任务1: with lock: // f(io_peration1) // f(io_operation2) f(processing on generated output files_level1)任务2: with lock: // f(io_peration level1 files) // f(io_operation level1 files) f(processing on generated output files_level2) - Rahuketu86
我们正在尝试实现一个插件架构,其中我可以将一些Task1.py Task2.py放在插件目录中。因此,我希望将其抽象为一个单独的函数,该函数可以循环调用所有任务。我有点新手,对Python不太熟悉,还不熟悉整个API。我会再探索一下setDaemon。但是感谢您的回复。 - Rahuketu86
@Rahuketu86:如果必须在每个线程内进行锁定,但同时对编写Task1.py等的人员透明,您可能需要编写一个简单的包装函数(或类),该函数接受插件模块/类/函数并使用锁调用其代码。(如果您不需要它是“透明”的,只需要是“简单”的,也许只需编写一个装饰器,这样每个插件只需添加一个@lockable_plugin行即可。) - abarnert
[link]http://stackoverflow.com/questions/16403593/with-statement-and-threading-making-function-execute-before-run - Rahuketu86
我已经发布了一个解决方案,使用上下文管理器抽象出线程。现在我将尝试结合您的建议来实现一些功能,使得Task2 / Task4 依赖于Task1。当然,机制必须以一种方式进行抽象,以便用户可以在配置文件中指定依赖项,并通过装饰器在代码中处理其余部分。顺便问一下,您对新问题有什么评论吗? - Rahuketu86

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接