本文共 3182 字,大约阅读时间需要 10 分钟。
全局解释器锁(GIL)是Python解释器的核心机制,它限制了并发线程对Python对象的访问,确保线程安全。GIL每100字节码指令周期进行一次锁释放和重新获取,尽管这一机制有效防止了并发安全问题,但也限制了多线程程序的性能表现。
理解线程与进程的区别是掌握线程编程的第一步。线程与进程在资源共享方面存在根本差异:线程共享同一内存空间和资源,但各线程间的状态一致;而进程则拥有独立的内存空间和资源。线程的轻量级特性使其在资源利用上更高效,但同时也带来了竞态条件、死锁等复杂问题。
Python线程的基本使用方法可以通过以下示例来体现:
import threadingimport datetimeclass ThreadClass(threading.Thread): def run(self): now = datetime.datetime.now() print(f"{self.getName()} says Hello World at time: {now}") t1 = ThreadClass()t2 = ThreadClass()t1.start()t2.start() 输出结果显示,两个线程能够同时输出"Hello World",并带有时间戳。这个简单的示例展示了线程的基本使用方式,类ThreadClass通过继承threading.Thread创建了一个线程池,run方法定义了线程执行的操作。
线程编程的复杂性在于需要处理共享资源的安全访问。队列模块提供了一种简便的解决方案,通过将任务传递给各个线程,简化了线程间的通信与同步。在以下示例中,使用队列实现了并发访问URL的处理:
import urllib2import timeimport Queueimport threadinghosts = ["http://yahoo.com", "http://baidu.com", "http://amazon.com", "http://ibm.com", "http://apple.com"]queue = Queue.Queue()class ThreadUrl(threading.Thread): def __init__(self, queue): threading.Thread.__init__(self) self.queue = queue def run(self): while True: host = self.queue.get() url = urllib2.urlopen(host) print(url.read(1024)) self.queue.task_done() start = time.time()def main(): for _ in range(5): t = ThreadUrl(queue) t.setDaemon(True) t.start() for host in hosts: queue.put(host) queue.join()main()print(f"Elapsed Time: {time.time() - start}") 在这个示例中,主线程创建了5个守护线程池,负责处理URL请求。每个线程从队列中获取URL,处理完毕后调用task_done。主线程通过队列.join()等待所有任务完成。
线程编程的高级特性可以通过组合多个队列实现更复杂的任务流程。例如,下面的示例展示了如何将多个队列用于数据处理:
import Queueimport threadingimport urllib2from BeautifulSoup import BeautifulSouphosts = ["http://yahoo.com", "http://baidu.com", "http://amazon.com", "http://ibm.com", "http://apple.com"]queue = Queue.Queue()out_queue = Queue.Queue()class ThreadUrl(threading.Thread): def __init__(self, queue, out_queue): threading.Thread.__init__(self) self.queue = queue self.out_queue = out_queue def run(self): while True: host = self.queue.get() url = urllib2.urlopen(host) chunk = url.read() self.out_queue.put(chunk) self.queue.task_done()class DatamineThread(threading.Thread): def __init__(self, out_queue): threading.Thread.__init__(self) self.out_queue = out_queue def run(self): while True: chunk = self.out_queue.get() soup = BeautifulSoup(chunk) print(f"Found: {len(soup.findAll(['title']))} titles") self.out_queue.task_done()def main(): for _ in range(5): t = ThreadUrl(queue, out_queue) t.setDaemon(True) t.start() for host in hosts: queue.put(host) for _ in range(5): dt = DatamineThread(out_queue) dt.setDaemon(True) dt.start() queue.join() out_queue.join()main()print(f"Elapsed Time: {time.time() - start}") 在这个扩展示例中,使用了两个队列:一个用于分发URL请求,另一个用于存储和处理获取的网页内容。这种模式展示了如何通过多级队列实现任务流水线,提高了并发处理效率。
线程编程的关键在于正确管理共享资源和避免竞争条件。通过使用队列模块,可以显著降低线程编程的复杂性,实现更高效的资源利用。在实际应用中,选择线程还是进程取决于任务的特性,进程适合需要长时间运行或资源密集型任务,而线程适合需要频繁通信的轻量级任务。
转载地址:http://kiofk.baihongyu.com/