1. Python并发编程简介
在计算机程序中,线程是被操作系统独立调度和分派的最小单位,进程是操作系统资源分配的基本单位。并发编程(Concurrency programming)就是指程序的设计和实现中有多个线程同时执行的情况下的编程模式,其目的是提高程序的性能、可伸缩性和响应能力。
1.1 Python多线程模块
Python的多线程模块有两个——Thread和Threadiing。Thread是比较低级的模块,而Threadiing更高层一点,对Thread进行了封装。使用Threadiing可以比较方便的进行多线程编程,而Thread则需要通过锁、信号量等操作来处理多线程之间的交互。
import threading
def worker():
print 'hello world'
threads = []
for i in range(5):
t = threading.Thread(target=worker)
threads.append(t)
t.start()
上面的例子创建了5个线程,每个线程都会调用worker函数打印“hello world”。
1.2 Python协程模块
协程是一种轻量级的线程,它可以在单线程中实现并发,并且具有比线程更小的上下文切换开销。Python的协程模块有Greenlet、gevent、asyncio等。其中Greenlet是一个比较底层的模块,gevent则在其基础上进行了封装,asyncio是Python 3.4之后引入的协程模块。
from gevent import monkey; monkey.patch_all()
import urllib2
import gevent
def fetch(url):
print 'fetching %s' % url
response = urllib2.urlopen(url)
data = response.read()
print '%s bytes received from %s.' % (len(data), url)
urls = ['https://github.com/', 'https://www.python.org/', 'https://www.baidu.com/']
jobs = [gevent.spawn(fetch, url) for url in urls]
gevent.joinall(jobs)
上面的例子使用gevent模块,创建3个协程同时请求3个网页,每个协程休眠1秒之后输出网页内容。
2. Python并发编程的应用
2.1 网络编程
在Python中,网络编程是并发编程的一个重要应用。Python的套接字模块socket是网络编程的核心之一,它支持多种传输协议和传输方式。在实际应用中,我们通常使用非阻塞式的套接字来编写高性能的网络程序。
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(('0.0.0.0', 8000))
server_socket.listen(5)
while True:
client_socket, address = server_socket.accept()
client_socket.setblocking(False)
data = ''
while True:
try:
buf = client_socket.recv(1024)
if not buf:
break
data += buf
except Exception as e:
break
print 'Received data:', data
上面的例子是一个简单的TCP服务器,每当有一个客户端连接,就创建一个新线程进行处理。使用socket.setblocking(False)将套接字设置为非阻塞式,可以避免线程被客户端占据而无法继续执行。
2.2 多进程编程
Python的多进程编程也是并发编程的重要应用之一,主要是使用multiprocessing模块来创建进程。
import multiprocessing
def worker():
print 'worker running.'
p = multiprocessing.Process(target=worker)
p.start()
上面的例子创建了一个进程,并在这个进程中执行worker函数。
2.3 GUI编程
Python常用的GUI库有Tkinter、wxPython、PyQt等。在GUI编程中,需要经常进行图形更新和事件响应等操作,这就需要使用多线程或协程来保证页面的流畅和响应。
import wx
import threading
class MyFrame(wx.Frame):
def __init__(self, parent, title):
wx.Frame.__init__(self, parent, title=title)
self.panel = wx.Panel(self)
self.btn = wx.Button(self.panel, label='Click')
self.btn.Bind(wx.EVT_BUTTON, self.OnBtnClick)
def OnBtnClick(self, event):
self.btn.Disable()
t = threading.Thread(target=self.HeavyTask)
t.start()
def HeavyTask(self):
for i in range(1, 11):
wx.CallAfter(self.UpdateUI, i)
time.sleep(1)
def UpdateUI(self, i):
self.panel.SetBackgroundColour('green' if i%2==0 else 'white')
self.panel.Refresh()
self.panel.Update()
self.btn.SetLabel('Working...%d' % i)
app = wx.App(False)
frame = MyFrame(None, 'GUI App')
frame.Show()
app.MainLoop()
上面的例子是一个简单的GUI程序,在单击按钮时,创建一个新线程,并在这个线程中执行重型任务,同时更新GUI界面。
3. Python并发编程的注意事项
3.1 共享资源
并发编程中必须处理多线程或协程共享资源的问题,例如公共变量、网络连接、数据库连接等,如果共享不当会导致数据不一致或死锁等问题。
3.2 线程安全
并发编程中应该注意程序的线程安全问题,例如对于一组数据的并发读写,可能会产生竞态条件,导致数据不一致或者程序异常。
3.3 GIL锁
Python的全局解释器锁(Global Interpreter Lock,简称GIL)是一个互斥锁,它可以在任一时刻只允许一个线程执行Python字节代码,并防止多个线程同时执行字节码。由于GIL的存在,Python的多线程编程可以在单核CPU上实现并发,但无法利用多核CPU。
4. 总结
Python并发编程是一项非常重要的技能,其应用范围广泛,包括网络编程、多进程编程和GUI编程等。在实际应用中需要注意并发编程的各种问题,例如共享资源、线程安全和GIL锁等。选择合适的多线程或协程模块,并合理设计程序结构,可以大大提高程序的性能和可扩展性。