我们在上一章中看到使用threading.Thread.join()可以避免主程序在等待其它线程结束的过程中得到时间片。事实上,在线程编程中经常会遇到一个线程需要等待另一个线程中的情况。在这种情况下,需要重申的是,我们不希望等待的线程获得时间片。
解决的办法是使用条件变量,就像它的名称,代码使用它们来等待一些条件的成立。大多数线程系统都支持条件变量,python的threading包也不例外。
例如,pthreads包,使用pthread_cond来代表这种变量,并且有函数pthread_cond_wait(),它代表一个线程调用等待一个事件的发生;另一个函数,pthread_cond_signal(),另一个线程调用,代表声明等待的线程事件已经发生。
python中的条件变量较之C语言中的来说更容易使用,对于第一层次的条件变量来说,可以使用类threading.Condition,它能够在大多数的线程系统中良好的工作,但是使用这种条件变量需要进行额外的解锁和闭锁操作。
python提供了一种更高层次的类,threading.Event,它是对于threading.Condition的一种封装,而且在后台自动处理有关的锁操作。
接下去的例子将使用多线程来实现一个端口扫描器,多端口并行扫描,每个端口对应一个线程,同时程序可以控制线程的个数。如果主线程准备对一个新的端口进行扫描操作但是已经达到线程数的上限时,主线程会等待线程数减少到线程数上限以下再进行新端口的扫描,这个步骤通过条件变量来实现。
1 # portscanner.py: checks for active ports on a given machine; would be
2 # more realistic if checked several hosts at once; different threads
3 # check different ports; there is a self-imposed limit on the number of
4 # threads, and the event mechanism is used to wait if that limit is
5 # reached
6
7 # usage: python portscanner.py host maxthreads
8
9 import sys, threading, socket
10
11 class scanner(threading.Thread):
12 tlist = [] # list of all current scanner threads
13 maxthreads = int(sys.argv[2]) # max number of threads we’re allowing
14 evnt = threading.Event() # event to signal OK to create more threads
15 lck = threading.Lock() # lock to guard tlist
16 def __init__(self,tn,host):
17 threading.Thread.__init__(self)
18 self.threadnum = tn # thread ID/port number
19 self.host = host # checking ports on this host
20 def run(self):
21 s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
22 try:
23 s.connect((self.host, self.threadnum))
24 print "%d: successfully connected" % self.threadnum
25 s.close()
26 except:
27 print "%d: connection failed" % self.threadnum
28 # thread is about to exit; remove from list, and signal OK if we
29 # had been up against the limit
30 scanner.lck.acquire()
31 scanner.tlist.remove(self)
32 print "%d: now active --" % self.threadnum, scanner.tlist
33 if len(scanner.tlist) == scanner.maxthreads-1:
34 scanner.evnt.set()
35 scanner.evnt.clear()
36 scanner.lck.release()
37 def newthread(pn,hst):
38 scanner.lck.acquire()
39 sc = scanner(pn,hst)
40 scanner.tlist.append(sc)
41 scanner.lck.release()
42 sc.start()
43 print "%d: starting check" % pn
44 print "%d: now active --" % pn, scanner.tlist
45 newthread = staticmethod(newthread)
46
47 def main():
13
48 host = sys.argv[1]
49 for i in range(1,100):
50 scanner.lck.acquire()
51 print "%d: attempting check" % i
52 # check to see if we’re at the limit before starting a new thread
53 if len(scanner.tlist) >= scanner.maxthreads:
54 # too bad, need to wait until not at thread limit
55 print "%d: need to wait" % i
56 scanner.lck.release()
57 scanner.evnt.wait()
58 else:
59 scanner.lck.release()
60 scanner.newthread(i,host)
61 for sc in scanner.tlist:
62 sc.join()
63
64 if __name__ == ’__main__’:
65 main()
就像我们预想的那样,主程序会发现我们预设的线程数上限,这时候我们调用threading.Event.wait()。这时候主线程将被阻塞,它将不会再被分配时间片。直到现有的一些线程退出运行,我们调用threading.Event.set()和threading.Event.clear()方法,它将会把先前调用threading.Event.wait()方法的线程从sleep状态转换成Run状态,在我们的例子中,即此时主线程可以重新获得时间片了。
对于threading.Event.clear()的调用是必要的,不然的话在这之后调用threading.Event.wait()都会立刻返回。
其它的多线程类
Event.set()唤醒所有的正在等待事件的线程。这对于我们前面的例子来说没有什么影响,因为只有一个主线程在进行等待。但对于更一般的应用程序来说,我们有时候需要唤醒一个而不是所有的线程。出于这个目的,我们重新回到threading.Condition层而不是threading.Event层,使用前者我们可以在notify()和notifyall()之间进行选择。
对于后者,实际就是Event.set()调用的,而notify()则使得线程管理器只唤醒一个等待线程。
分享到:
相关推荐
python,python多线程Python多线程学习Python中的线程使用python多线程Python多线程学习Python中的线程使用
在Python编程中,多线程同步对于文件读写控制至关重要,因为如果不加以控制,多个线程同时访问同一文件可能会导致数据不一致或者错误。这里我们将深入探讨如何在Python中使用多线程同步来确保文件读写的安全性。 ...
本主题聚焦于如何使用Python的Selenium与Chrome浏览器进行多开和多线程操作,结合phantomjs和chromedriver这两个关键组件来实现。首先,让我们详细了解一下这些概念。 1. **Selenium**: Selenium是一个强大的Web...
Python里的多线程是假的多线程,不管有多少核,同一时间只能在一个核中进行操作!利用Python的多线程,只是利用CPU上下文切换的优势,看上去像是并发,其实只是个单线程,所以说他是假的单线程。 那么什么时候用多...
Python多线程编程在实现网络串口透传中扮演着重要的角色,特别是在处理TCP客户端网口数据时。网络串口透传技术允许通过网络连接模拟串行通信,使得远程设备可以像连接本地串口一样进行通信。这在物联网(IoT)、远程...
在Python编程中,多线程是一种并发执行任务的机制,尤其在处理I/O密集型任务如网络请求时,能够显著提高程序效率。本教程将详细讲解如何使用Python的多线程来实现对多个网址的快速访问,并记录访问结果。 首先,...
python3 多线程压缩文件
标题中的“单线程与多线程Python爬虫”涉及的核心是如何利用线程技术优化地图瓦片的下载过程。单线程爬虫适用于小型项目,其代码简单易懂,但当需要下载大量瓦片时,速度可能较慢。多线程爬虫则可以显著提升下载速度...
Python多线程编程是Python系统编程中的重要组成部分,它允许程序同时执行多个任务,从而提高程序的效率和响应速度。在Python中,我们通常使用内置的`thread`库、`threading`库以及`Queue`库来实现多线程。 首先,`...
在Python编程语言中,多线程是实现并发执行任务的重要机制。它允许程序同时执行多个不同的任务,从而提高效率和响应速度。在这个基于Python的多线程例子中,我们将深入探讨如何创建和管理线程,以及如何利用它们来...
总的来说,理解并正确使用锁和可重入锁是解决Python多线程安全问题的关键。在编写多线程程序时,合理地引入锁机制可以防止数据竞争,确保程序的正确性。但同时,过度使用锁可能导致程序的复杂性和性能下降,因此在...
总结起来,`python多线程定时器`的实现涉及到Python的`threading`和`time`模块,通过`Thread`和`Timer`类来创建和管理线程,使用定时器控制线程的启动时间。主程序`main.py`使用这些工具来安排多个任务按设定的间隔...
在探讨Python3中的多线程之前,我们首先需要理解线程的基本状态及其转换过程。 - **新建**: 当一个线程被创建后,它最初处于新建状态。 - **就绪**: 当线程被创建并准备好执行时,它会处于就绪状态,等待CPU分配...
本离线安装包“python多线程池离线安装包.zip”包含了实现Python多线程所需的关键组件。主要包含以下三个子文件: 1. `pip-19.2.3.tar.gz`:这是Python的包管理器pip的一个版本,用于安装和管理Python库。在离线...
通过深入学习和实践压缩包中的“PYthon multithreading Test”源码,你可以更好地掌握Python多线程的原理和应用,为编写高效、稳定的多线程程序打下坚实的基础。在实际开发中,结合具体场景选择合适的并发模型,是...
Python多线程编程,简要描述了Python中多线程的实现过程
Python 多线程编程实例,一个综合示例
此外,相比于依赖开放API的爬虫程序,基于Linux和Python多线程的爬虫程序在长期运行中显示出更好的性能。 在实现多线程爬虫时,需要考虑到多线程的同步和数据一致性问题。由于多线程程序在执行时会共享内存资源,...
### 利用Python SOCKET多线程开发FTP软件 在当今高度数字化的世界中,文件传输协议(FTP)作为互联网上的一项标准服务,被广泛用于在网络上进行文件传输。利用Python的socket库和多线程技术开发一个FTP软件,不仅...
在多线程编程中,我们需要注意全局解释器锁(GIL),它是Python解释器为了保证线程安全而引入的一个机制,但同时也限制了多线程在CPU密集型任务中的性能,因为GIL使得Python在同一时刻只能有一个线程在执行。...