一篇文章读懂 Python 多线程(4)
当你真正运行这段代码时,你会发现它只是挂起了。究其原因,是因为我们只告诉 threading 模块获取锁。所以当我们调用第一个函数时,它发现锁已经被获取,随后便把自己挂起了,直到锁被释放,然而这将永远不会发生。 真正的解决办法是使用重入锁(Re-Entrant Lock)。threading 模块提供的解决办法是使用RLock函数。即把lock = threading.lock替换为lock = threading.RLock,然后重新运行代码,现在代码就可以正常运行了。 如果你想在线程中运行以上代码,那么你可以用以下代码取代直接调用 main函数:
每个线程都会运行 main 函数,main 函数则会依次调用另外两个函数。最终也会产生 10 组结果集。 定时器 Threading 模块有一个优雅的 Timer类,你可以用它来实现在指定时间后要发生的动作。它们实际上会启动自己的自定义线程,通过调用常规线程上的start方法即可运行。你也可以调用它的cancel方法停止定时器。值得注意的是,你甚至可以在开始定时器之前取消它。 有一天,我遇到一个特殊的情况:我需要与已经启动的子进程通信,但是我需要它有超时处理。虽然处理这种特殊问题有很多不同的方法,不过我最喜欢的解决方案是使用 threading 模块的 Timer 类。 在下面这个例子中,我们将使用 ping指令作为演示。在 Linux 系统中,ping 命令会一直运行下去直到你手动杀死它。所以在 Linux 世界里,Timer 类就显得非常方便。示例如下:
这里我们在 lambda 表达式中调用 kill 杀死进程。接下来启动 ping 命令,然后创建 Timer 对象。你会注意到,第一个参数就是需要等待的秒数,第二个参数是需要调用的函数,紧跟其后的参数是要调用函数的入参。在本例中,我们的函数是一个 lambda 表达式,传入的是一个只有一个元素的列表。如果你运行这段代码,它应该会运行 5 秒钟,然后打印出 ping 的结果。 其他线程组件 Threading 模块包含对其他功能的支持。例如,你可以创建信号量(Semaphore),这是计算机科学中最古老的同步原语之一。基本上,一个信号量管理一个内置的计数器。当你调用acquire时计数器就会递减,相反当你调用release时就会递增。根据其设计,计数器的值无法小于零,所以如果正好在计数器为零时调用 acquire 方法,该方法将阻塞线程。 译者注:通常使用信号量时都会初始化一个大于零的值,如 semaphore = threading.Semaphore(2) 另一个非常有用的同步工具就是事件(Event)。它允许你使用信号(signal)实现线程通信。在下一节中我们将举一个使用事件的实例。 最后,在 Python 3.2 中加入了 Barrier对象。Barrier 是管理线程池中的同步原语,在线程池中多条线程需要相互等待对方。如果要传递 barrier,每一条线程都要调用wait方法,在其他线程调用该方法之前线程将会阻塞。全部调用之后将会同时释放所有线程。 线程通信 某些情况下,你会希望线程之间互相通信。就像先前提到的,你可以通过创建 Event对象达到这个目的。但更常用的方法是使用队列(Queue)。在我们的例子中,这两种方式都会有所涉及。下面让我们看看到底是什么样子的:
(编辑:ASP站长网) |