- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章使用Python中的线程进行网络编程的入门教程由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
引言 。
对于 Python 来说,并不缺少并发选项,其标准库中包括了对线程、进程和异步 I/O 的支持。在许多情况下,通过创建诸如异步、线程和子进程之类的高层模块,Python 简化了各种并发方法的使用。除了标准库之外,还有一些第三方的解决方案,例如 Twisted、Stackless 和进程模块。本文重点关注于使用 Python 的线程,并使用了一些实际的示例进行说明。虽然有许多很好的联机资源详细说明了线程 API,但本文尝试提供一些实际的示例,以说明一些常见的线程使用模式.
全局解释器锁 (Global Interpretor Lock) 说明 Python 解释器并不是线程安全的。当前线程必须持有全局锁,以便对 Python 对象进行安全地访问。因为只有一个线程可以获得 Python 对象/C API,所以解释器每经过 100 个字节码的指令,就有规律地释放和重新获得锁。解释器对线程切换进行检查的频率可以通过 sys.setcheckinterval() 函数来进行控制.
此外,还将根据潜在的阻塞 I/O 操作,释放和重新获得锁。有关更详细的信息,请参见参考资料部分中的 Gil and Threading State 和 Threading the Global Interpreter Lock.
需要说明的是,因为 GIL,CPU 受限的应用程序将无法从线程的使用中受益。使用 Python 时,建议使用进程,或者混合创建进程和线程.
首先弄清进程和线程之间的区别,这一点是非常重要的。线程与进程的不同之处在于,它们共享状态、内存和资源。对于线程来说,这个简单的区别既是它的优势,又是它的缺点。一方面,线程是轻量级的,并且相互之间易于通信,但另一方面,它们也带来了包括死锁、争用条件和高复杂性在内的各种问题。幸运的是,由于 GIL 和队列模块,与采用其他的语言相比,采用 Python 语言在线程实现的复杂性上要低得多。 使用 Python 线程 。
要继续学习本文中的内容,我假定您已经安装了 Python 2.5 或者更高版本,因为本文中的许多示例都将使用 Python 语言的新特性,而这些特性仅出现于 Python2.5 之后。要开始使用 Python 语言的线程,我们将从简单的 "Hello World" 示例开始: hello_threads_example 。
1
|
2
3
4
5
6
7
8
9
10
11
12
|
import
threading
import
datetime
class
ThreadClass(threading.Thread):
def
run(
self
):
now
=
datetime.datetime.now()
print
"%s says Hello World at time: %s"
%
(
self
.getName(), now)
for
i
in
range
(
2
):
t
=
ThreadClass()
t.start()
|
如果运行这个示例,您将得到下面的输出:
1
|
2
3
|
# python hello_threads.py
Thread
-
1
says Hello World at time:
2008
-
05
-
13
13
:
22
:
50.252069
Thread
-
2
says Hello World at time:
2008
-
05
-
13
13
:
22
:
50.252576
|
仔细观察输出结果,您可以看到从两个线程都输出了 Hello World 语句,并都带有日期戳。如果分析实际的代码,那么将发现其中包含两个导入语句;一个语句导入了日期时间模块,另一个语句导入线程模块。类 ThreadClass 继承自 threading.Thread,也正因为如此,您需要定义一个 run 方法,以此执行您在该线程中要运行的代码。在这个 run 方法中唯一要注意的是,self.getName() 是一个用于确定该线程名称的方法.
最后三行代码实际地调用该类,并启动线程。如果注意的话,那么会发现实际启动线程的是 t.start()。在设计线程模块时考虑到了继承,并且线程模块实际上是建立在底层线程模块的基础之上的。对于大多数情况来说,从 threading.Thread 进行继承是一种最佳实践,因为它创建了用于线程编程的常规 API。 使用线程队列 。
如前所述,当多个线程需要共享数据或者资源的时候,可能会使得线程的使用变得复杂。线程模块提供了许多同步原语,包括信号量、条件变量、事件和锁。当这些选项存在时,最佳实践是转而关注于使用队列。相比较而言,队列更容易处理,并且可以使得线程编程更加安全,因为它们能够有效地传送单个线程对资源的所有访问,并支持更加清晰的、可读性更强的设计模式.
在下一个示例中,您将首先创建一个以串行方式或者依次执行的程序,获取网站的 URL,并显示页面的前 1024 个字节。有时使用线程可以更快地完成任务,下面就是一个典型的示例。首先,让我们使用 urllib2 模块以获取这些页面(一次获取一个页面),并且对代码的运行时间进行计时: URL 获取序列 。
1
|
2
3
4
5
6
7
8
9
10
11
12
13
|
import
urllib2
import
time
hosts
=
[
"
http://yahoo.com
"
,
"
http://google.com
"
,
"
http://amazon.com
"
,
"
http://ibm.com
"
,
"
http://apple.com
"
]
start
=
time.time()
#grabs urls of hosts and prints first 1024 bytes of page
for
host
in
hosts:
url
=
urllib2.urlopen(host)
print
url.read(
1024
)
print
"Elapsed Time: %s"
%
(time.time()
-
start)
|
在运行以上示例时,您将在标准输出中获得大量的输出结果。但最后您将得到以下内容:
1
|
|
Elapsed Time:
2.40353488922
|
让我们仔细分析这段代码。您仅导入了两个模块。首先,urllib2 模块减少了工作的复杂程度,并且获取了 Web 页面。然后,通过调用 time.time(),您创建了一个开始时间值,然后再次调用该函数,并且减去开始值以确定执行该程序花费了多长时间。最后分析一下该程序的执行速度,虽然“2.5 秒”这个结果并不算太糟,但如果您需要检索数百个 Web 页面,那么按照这个平均值,就需要花费大约 50 秒的时间。研究如何创建一种可以提高执行速度的线程化版本: URL 获取线程化 。
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
|
#!/usr/bin/env python
import
Queue
import
threading
import
urllib2
import
time
hosts
=
[
"
http://yahoo.com
"
,
"
http://google.com
"
,
"
http://amazon.com
"
,
"
http://ibm.com
"
,
"
http://apple.com
"
]
queue
=
Queue.Queue()
class
ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def
__init__(
self
, queue):
threading.Thread.__init__(
self
)
self
.queue
=
queue
def
run(
self
):
while
True
:
#grabs host from queue
host
=
self
.queue.get()
#grabs urls of hosts and prints first 1024 bytes of page
url
=
urllib2.urlopen(host)
print
url.read(
1024
)
#signals to queue job is done
self
.queue.task_done()
start
=
time.time()
def
main():
#spawn a pool of threads, and pass them queue instance
for
i
in
range
(
5
):
t
=
ThreadUrl(queue)
t.setDaemon(
True
)
t.start()
#populate queue with data
for
host
in
hosts:
queue.put(host)
#wait on the queue until everything has been processed
queue.join()
main()
print
"Elapsed Time: %s"
%
(time.time()
-
start)
|
对于这个示例,有更多的代码需要说明,但与第一个线程示例相比,它并没有复杂多少,这正是因为使用了队列模块。在 Python 中使用线程时,这个模式是一种很常见的并且推荐使用的方式。具体工作步骤描述如下:
在使用这个模式时需要注意一点:通过将守护线程设置为 true,将允许主线程或者程序仅在守护线程处于活动状态时才能够退出。这种方式创建了一种简单的方式以控制程序流程,因为在退出之前,您可以对队列执行 join 操作、或者等到队列为空。队列模块文档详细说明了实际的处理过程,请参见参考资料:
join() 保持阻塞状态,直到处理了队列中的所有项目为止。在将一个项目添加到该队列时,未完成的任务的总数就会增加。当使用者线程调用 task_done() 以表示检索了该项目、并完成了所有的工作时,那么未完成的任务的总数就会减少。当未完成的任务的总数减少到零时,join() 就会结束阻塞状态.
使用多个队列 。
因为上面介绍的模式非常有效,所以可以通过连接附加线程池和队列来进行扩展,这是相当简单的。在上面的示例中,您仅仅输出了 Web 页面的开始部分。而下一个示例则将返回各线程获取的完整 Web 页面,然后将结果放置到另一个队列中。然后,对加入到第二个队列中的另一个线程池进行设置,然后对 Web 页面执行相应的处理。这个示例中所进行的工作包括使用一个名为 Beautiful Soup 的第三方 Python 模块来解析 Web 页面。使用这个模块,您只需要两行代码就可以提取所访问的每个页面的 title 标记,并将其打印输出。 多队列数据挖掘网站 。
1
|
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
|
import
Queue
import
threading
import
urllib2
import
time
from
BeautifulSoup
import
BeautifulSoup
hosts
=
[
"
http://yahoo.com
"
,
"
http://google.com
"
,
"
http://amazon.com
"
,
"
http://ibm.com
"
,
"
http://apple.com
"
]
queue
=
Queue.Queue()
out_queue
=
Queue.Queue()
class
ThreadUrl(threading.Thread):
"""Threaded Url Grab"""
def
__init__(
self
, queue, out_queue):
threading.Thread.__init__(
self
)
self
.queue
=
queue
self
.out_queue
=
out_queue
def
run(
self
):
while
True
:
#grabs host from queue
host
=
self
.queue.get()
#grabs urls of hosts and then grabs chunk of webpage
url
=
urllib2.urlopen(host)
chunk
=
url.read()
#place chunk into out queue
self
.out_queue.put(chunk)
#signals to queue job is done
self
.queue.task_done()
class
DatamineThread(threading.Thread):
"""Threaded Url Grab"""
def
__init__(
self
, out_queue):
threading.Thread.__init__(
self
)
self
.out_queue
=
out_queue
def
run(
self
):
while
True
:
#grabs host from queue
chunk
=
self
.out_queue.get()
#parse the chunk
soup
=
BeautifulSoup(chunk)
print
soup.findAll([
'title'
])
#signals to queue job is done
self
.out_queue.task_done()
start
=
time.time()
def
main():
#spawn a pool of threads, and pass them queue instance
for
i
in
range
(
5
):
t
=
ThreadUrl(queue, out_queue)
t.setDaemon(
True
)
t.start()
#populate queue with data
for
host
in
hosts:
queue.put(host)
for
i
in
range
(
5
):
dt
=
DatamineThread(out_queue)
dt.setDaemon(
True
)
dt.start()
#wait on the queue until everything has been processed
queue.join()
out_queue.join()
main()
print
"Elapsed Time: %s"
%
(time.time()
-
start)
|
如果运行脚本的这个版本,您将得到下面的输出:
1
|
2
3
4
5
6
7
8
9
|
# python url_fetch_threaded_part2.py
[<title>Google<
/
title>]
[<title>Yahoo!<
/
title>]
[<title>Apple<
/
title>]
[<title>IBM United States<
/
title>]
[<title>Amazon.com: Online Shopping
for
Electronics, Apparel,
Computers, Books, DVDs & more<
/
title>]
Elapsed Time:
3.75387597084
|
分析这段代码时您可以看到,我们添加了另一个队列实例,然后将该队列传递给第一个线程池类 ThreadURL。接下来,对于另一个线程池类 DatamineThread,几乎复制了完全相同的结构。在这个类的 run 方法中,从队列中的各个线程获取 Web 页面、文本块,然后使用 Beautiful Soup 处理这个文本块。在这个示例中,使用 Beautiful Soup 提取每个页面的 title 标记、并将其打印输出。可以很容易地将这个示例推广到一些更有价值的应用场景,因为您掌握了基本搜索引擎或者数据挖掘工具的核心内容。一种思想是使用 Beautiful Soup 从每个页面中提取链接,然后按照它们进行导航.
总结 。
本文研究了 Python 的线程,并且说明了如何使用队列来降低复杂性和减少细微的错误、并提高代码可读性的最佳实践。尽管这个基本模式比较简单,但可以通过将队列和线程池连接在一起,以便将这个模式用于解决各种各样的问题。在最后的部分中,您开始研究如何创建更复杂的处理管道,它可以用作未来项目的模型。参考资料部分提供了很多有关常规并发性和线程的极好的参考资料.
最后,还有很重要的一点需要指出,线程并不能解决所有的问题,对于许多情况,使用进程可能更为合适。特别是,当您仅需要创建许多子进程并对响应进行侦听时,那么标准库子进程模块可能使用起来更加容易。有关更多的官方说明文档,请参考参考资料部分.
最后此篇关于使用Python中的线程进行网络编程的入门教程的文章就讲到这里了,如果你想了解更多关于使用Python中的线程进行网络编程的入门教程的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我将 Bootstrap 与 css 和 java 脚本结合使用。在不影响前端代码的情况下,我真的很难在css中绘制这个背景。在许多问题中,人们将宽度和高度设置为 0%。但是由于我的导航栏,我不能使用
我正在用 c 编写一个程序来读取文件的内容。代码如下: #include void main() { char line[90]; while(scanf("%79[^\
我想使用 javascript 获取矩阵数组的所有对 Angular 线。假设输入输出如下: input = [ [1,2,3], [4,5,6], [7,8,9], ] output =
可以用pdfmake绘制lines,circles和other shapes吗?如果是,是否有documentation或样本?我想用jsPDF替换pdfmake。 最佳答案 是的,有可能。 pdfm
我有一个小svg小部件,其目的是显示角度列表(参见图片)。 现在,角度是线元素,仅具有笔触,没有填充。但是现在我想使用一种“内部填充”颜色和一种“笔触/边框”颜色。我猜想line元素不能解决这个问题,
我正在为带有三角对象的 3D 场景编写一个非常基本的光线转换器,一切都工作正常,直到我决定尝试从场景原点 (0/0/0) 以外的点转换光线。 但是,当我将光线原点更改为 (0/1/0) 时,相交测试突
这个问题已经有答案了: Why do people write "#!/usr/bin/env python" on the first line of a Python script? (22 个回
如何使用大约 50 个星号 * 并使用 for 循环绘制一条水平线?当我尝试这样做时,结果是垂直(而不是水平)列出 50 个星号。 public void drawAstline() { f
这是一个让球以对角线方式下降的 UI,但球保持静止;线程似乎无法正常工作。你能告诉我如何让球移动吗? 请下载一个球并更改目录,以便程序可以找到您的球的分配位置。没有必要下载足球场,但如果您愿意,也可以
我在我的一个项目中使用 Jmeter 和 Ant,当我们生成报告时,它会在报告中显示 URL、#Samples、失败、成功率、平均时间、最短时间、最长时间。 我也想在报告中包含 90% 的时间线。 现
我有一个不寻常的问题,希望有人能帮助我。我想用 Canvas (android) 画一条 Swing 或波浪线,但我不知道该怎么做。它将成为蝌蚪的尾部,所以理想情况下我希望它的形状更像三角形,一端更大
这个问题已经有答案了: Checking Collision of Shapes with JavaFX (1 个回答) 已关闭 8 年前。 我正在使用 JavaFx 8 库。 我的任务很简单:我想检
如何按编号的百分比拆分文件。行数? 假设我想将我的文件分成 3 个部分(60%/20%/20% 部分),我可以手动执行此操作,-_-: $ wc -l brown.txt 57339 brown.tx
我正在努力实现这样的目标: 但这就是我设法做到的。 你能帮我实现预期的结果吗? 更新: 如果我删除 bootstrap.css 依赖项,问题就会消失。我怎样才能让它与 Bootstrap 一起工作?
我目前正在构建一个网站,但遇到了 transform: scale 的问题。我有一个按钮,当用户将鼠标悬停在它上面时,会发生两件事: 背景以对 Angular 线“扫过” 按钮标签颜色改变 按钮稍微变
我需要使用直线和仿射变换绘制大量数据点的图形(缩放图形以适合 View )。 目前,我正在使用 NSBezierPath,但我认为它效率很低(因为点在绘制之前被复制到贝塞尔路径)。通过将我的数据切割成
我正在使用基于 SVM 分类的 HOG 特征检测器。我可以成功提取车牌,但提取的车牌除了车牌号外还有一些不必要的像素/线。我的图像处理流程如下: 在灰度图像上应用 HOG 检测器 裁剪检测到的区域 调
我有以下图片: 我想填充它的轮廓(即我想在这张图片中填充线条)。 我尝试了形态学闭合,但使用大小为 3x3 的矩形内核和 10 迭代并没有填满整个边界。我还尝试了一个 21x21 内核和 1 迭代,但
我必须找到一种算法,可以找到两组数组之间的交集总数,而其中一个数组已排序。 举个例子,我们有这两个数组,我们向相应的数字画直线。 这两个数组为我们提供了总共 7 个交集。 有什么样的算法可以帮助我解决
简单地说 - 我想使用透视投影从近裁剪平面绘制一条射线/线到远裁剪平面。我有我认为是使用各种 OpenGL/图形编程指南中描述的方法通过单击鼠标生成的正确标准化的世界坐标。 我遇到的问题是我的光线似乎
我是一名优秀的程序员,十分优秀!