13 线程和进程初探
进程和线程初探
概念
-
进程就是操作系统中执行的一个程序,操作系统以进程为单位分配存储空间,每个进程都有自己的地址空间、数据栈以及其他用于跟踪进程执行的辅助数据,操作系统管理所有进程的执行,为它们合理的分配资源。进程可以通过fork或spawn的方式来创建新的进程来执行其他的任务,不过新的进程也有自己独立的内存空间,因此必须通过进程间通信机制(IPC,Inter-Process Communication)来实现数据共享,具体的方式包括管道、信号、套接字、共享内存区等。
一个进程还可以拥有多个并发的执行线索,简单的说就是拥有多个可以获得CPU调度的执行单元,这就是所谓的线程。由于线程在同一个进程下,它们可以共享相同的上下文,因此相对于进程而言,线程间的信息共享和通信更加容易。当然在单核CPU系统中,真正的并发是不可能的,因为在某个时刻能够获得CPU的只有唯一的一个线程,多个线程共享了CPU的执行时间。使用多线程实现并发编程为程序带来的好处是不言而喻的,最主要的体现在提升程序的性能和改善用户体验,今天我们使用的软件几乎都用到了多线程技术,这一点可以利用系统自带的进程监控工具(如macOS中的“活动监视器”、Windows中的“任务管理器”)来证实。
-
python中有GIL锁,每次cpu只能执行一个线程,多线程的应用场景是不需要cpu算力的场景,多进程则是大量运算的场景。
-
可以分为:多进程、多线程、多进程多线程
多进程
-
单进程和多进程的区别
#单进程和多进程的差别 #单进程运行 from random import randint from time import time,sleep def download_task(filename): print('开始下载%s'%filename) time_to_download=randint(5,10) sleep(time_to_download) print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) def main(): start=time() download_task('Python从入门到住院.pdf') download_task('Peking Hot.avi') end=time() print('总共耗费了%.2f秒.' % (end - start)) if __name__ == '__main__': main() ''' 开始下载Python从入门到住院.pdf Python从入门到住院.pdf下载完成! 耗费了5秒 开始下载Peking Hot.avi Peking Hot.avi下载完成! 耗费了5秒 总共耗费了10.01秒. ''' #多进程运行 from multiprocessing import Process from os import getpid from random import randint from time import time,sleep def download_task(filename): print('启动进程,进程号为%d'%getpid()) print('开始下载%s...' % filename) time_to_download=randint(5,10) sleep(time_to_download) print('%s下载完成! 耗费了%d秒' % (filename, time_to_download)) def main(): start=time() p1=Process(target=download_task,args=('文件1',)) p1.start() p2=Process(target=download_task,args=('文件2',)) p2.start() p1.join() p2.join() end=time() if __name__ == '__main__': main() ''' 启动进程,进程号为83376 开始下载文件1... 启动进程,进程号为83377 开始下载文件2... 文件2下载完成! 耗费了7秒 文件1下载完成! 耗费了7秒 总共耗费了7.02秒. '''
多线程
# 和多进程中一样,此时使用的模块有变化
from random import randint
from threading import Thread
from time import time, sleep
def download(filename):
print('开始下载%s...' % filename)
time_to_download = randint(5, 10)
sleep(time_to_download)
print('%s下载完成! 耗费了%d秒' % (filename, time_to_download))
def main():
start = time()
t1 = Thread(target=download, args=('Python从入门到住院.pdf',))
t1.start()
t2 = Thread(target=download, args=('Peking Hot.avi',))
t2.start()
t1.join()
t2.join()
end = time()
print('总共耗费了%.3f秒' % (end - start))
if __name__ == '__main__':
main()
'''
开始下载Python从入门到住院.pdf...
开始下载Peking Hot.avi...
Python从入门到住院.pdf下载完成! 耗费了9秒
Peking Hot.avi下载完成! 耗费了9秒
总共耗费了9.004秒
'''
-
因为多个线程可以共享进程的内存空间,因此要实现多个线程间的通信相对简单,大家能想到的最直接的办法就是设置一个全局变量,多个线程共享这个全局变量即可。但是当多个线程共享同一个变量(我们通常称之为“资源”)的时候,很有可能产生不可控的结果从而导致程序失效甚至崩溃。如果一个资源被多个线程竞争使用,那么我们通常称之为“临界资源”,对“临界资源”的访问需要加上保护,否则资源会处于“混乱”的状态。下面的例子演示了100个线程向同一个银行账户转账(转入1元钱)的场景,在这个例子中,银行账户就是一个临界资源,在没有保护的情况下我们很有可能会得到错误的结果,因此需要加锁。
from time import sleep from threading import Thread,Lock class Account(): def __init__(self): #这是一个余额是0的账户 self._balance=0 self._lock=Lock() @property def balance(self): return self._balance #存款行为 def deposit(self,money): self._lock.acquire() try: new_balance=self._balance+money sleep(0.01) self._balance=new_balance finally: self._lock.release() class AddMoneyThread(Thread): def __init__(self,account,money): super().__init__() self._account=account self._money=money def run(self): self._account.deposit(self._money) def main(): account=Account() threads=[] #创建100个线程同时往账户里存钱 for _ in range(100): t=AddMoneyThread(account,1) threads.append(t) t.start() # 等所有存款的线程都执行完毕 for t in threads: t.join() print('账户余额为: ¥%d元' % account.balance) if __name__ == '__main__': main()