python菜鸟学习Day8(进程和线程)

进程和线程

每一个应用程序就是一个进程,操作系统以进程为单位分配存储空间和资源。程序是一堆指令的集合,操作系统将程序调入内存,为其分配资源,就是进程。多进程就是系统允许将多个程序同时加载到内存,在系统调度下并发的执行。
为什么还需要线程:进程只能在一个时间执行一件事。比如打开qq这个进程,它即需要监听你键盘的输入,又要监听其他人发给你的消息等,比如Word,它可以同时进行打字、拼写检查、打印等事情。不止一个线程(子任务)

每个进程都有独立的代码和存储空间,进程之间共享内存,硬盘,打印机等资源。
线程共享代码和存储空间,线程所使用的资源来自于所属进程的资源。
在操作系统中能同时运行多个进程(程序):而在同一个进程中有多个线程同时执行。

每一个进程启动时,都会最先创建一个线程,叫主线程,主线程再创建其他线程,一个进程有且只有一个主线程。

其他语言cpu是多核时,支持多线程同时进行,但python,无论cpu是单核还是多核,同时只能由一个线程在执行,根源是 GIL (全局解释器)的存在。某个线程要执行,就必须获得GIL,在一个进程中,只有一个GIL拿不到通行证的线程,就不允许进入 CPU 执行。

进程

进程可以通过Process创建,也可以通过Pool创建进程池批量创建进程。不同进程分配不同的内存,进程间通信通过队列(Queue)和管道(Pipe)等.

from multiprocessing import Process,Pool,Queue
from threading import Thread
from random import randint
import time,os
def downLoadTask(filename):
	print('启动下载进程,进程号[%d]' % os.getpid())
	timeDownLoad = randint(5,10)
	print('开始下载%s' % filename)
	time.sleep(timeDownLoad)
	print('%s下载完成,耗时%d!!!' % (filename,timeDownLoad))
def main():
	startTime =  time.time()
	p1 = Process(target=downLoadTask,args=('从入门到放弃',))
	p1.start()
	p2 = Process(target=downLoadTask,args=('从弃到成功',))
	p2.start()
	p1.join()
	p2.join()
	endTime = time.time()
	print('共耗时%d' % (endTime-startTime))
if __name__ == '__main__':
	main()

用Pool批量创建进程

from multiprocessing import Process,Pool,Queue
from threading import Thread
from random import randint
import time,os
def downLoadTask(filename):
	print('启动下载进程,进程号[%d]' % os.getpid())
	timeDownLoad = randint(5,10)
	print('开始下载%s' % filename)
	time.sleep(timeDownLoad)
	print('%s下载完成,耗时%d!!!' % (filename,timeDownLoad))
def main2():
	startTime = time.time()
	poolList = Pool(3)
	fileNameList = ['从入门到放弃','从放弃到成功','从成功到巅峰']
	for name in fileNameList:
		poolList.apply_async(downLoadTask,args=(name,))
	poolList.close()
	poolList.join()
	endTime = time.time()
	print('共耗时%d' % (endTime - startTime))
if __name__ == '__main__':
	main2()

进程间通过Quene通信

from multiprocessing import Process,Pool,Queue
from threading import Thread
from random import randint
import time,os
def writeString(q,stringLst):
	for one in stringLst:
		print('Put %s to queue...' % one)
		q.put(one)#将数据装进quene的一端
		time.sleep(randint(1,2))
	q.put(None) #进程的最后加入一个结束信号
def readString(q):
	while True: #一直监听是否还有数据
		s = q.get(True)#从quene的另一端获取数据
		if s == None:
			break
		else:
			print('Get %s from queue.' % s)
def main():
	if __name__ == '__main__':
		# 父进程创建Queue,并传给各个子进程:
		q = Queue()
		pw = Process(target=writeString, args=(q,['Lining','Anta','361']))
		# 启动子进程pw,写入:
		pw.start()
		pr = Process(target=readString, args=(q,))
		# 启动子进程pr,读取:
		pr.start()
		# 等待pw结束:
		pw.join()
		#等待pr结束
		pr.join()
if __name__ == '__main__':
	main()

多线程
多线程可以共用进程的内存资源,因此,线程的通信很简单,最直接的办法是创建一个全局变量,线程共享这个全局变量即可。但当多个线程共享一个变量(资源)的时候,很可能产生不可控的结果。如果一个资源被多个线程竞争使用,这个资源需要加上防护,即锁,否则多个线程同时操作这个资源,会使资源混乱。
这个例子展示中,账户金额就是资源。

import time
from threading import Thread,Lock
class Account(object):
	def __init__(self):
		self._balance = 0
	@property
	def balance(self):
		return  self._balance
	def deposit(self,money):
		#改变账户余额,首先获取锁
		try:
			newBalance = self._balance + money
			time.sleep(0.1)
			self._balance = newBalance
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()
	threadLst = []
	for _ in range(100):
		t = AddMoneyThread(account,1)

		t.start()
		threadLst.append(t)
	for i in threadLst:
		i.join()
	print('账户余额为: ¥%d元' % account.balance)
if __name__ == "__main__":
	main()

100个线程分别向账户中转入1元钱,可以看到账户金额远远小于100 。多个线程同时向账户中存钱,执行newBalance = self._balance + money时, self._balance 的初始状态都是0.所以结果错误。因此,需要锁,Lock()对资源加以保护,只有获得锁的线程A才能操作资源lock.acquire(),其他线程阻塞起来,等线程A操作完毕后释放锁lock.release(),其他线程才有机会获取锁操作资源。

import time
from threading import Thread,Lock
class Account(object):
	def __init__(self):
		self._balance = 0
		self._lock = Lock()
	@property
	def balance(self):
		return  self._balance
	def deposit(self,money):
		#改变账户余额,首先获取锁
		self._lock.acquire()
		try:
			newBalance = self._balance + money
			time.sleep(0.1)
			self._balance = newBalance
		finally:
			# 在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()
	threadLst = []
	for _ in range(100):
		t = AddMoneyThread(account,1)

		t.start()
		threadLst.append(t)
	for i in threadLst:
		i.join()
	print('账户余额为: ¥%d元' % account.balance)
if __name__ == "__main__":
	main()

无论是多线程还是多进程,只要数量一多,效率肯定上不去,因为操作系统在进行进程或线程切换时,需要先保存当前执行的现场环境(内存,寄存器等),然后把新任务的执行环境准备好,这个切换虽然很快,但也是需要时间的。如果将任务分成计算密集型和I/O密集型。计算密集型需要大量计算,消耗CPU资源,效率全靠CPU能力,任务越多,花在切换上时间越多,效率越低。I/O密集型,任务特点,CPU消耗很少,大多时间在等待I/O操作完成,因此对于I/O密集型任务,可以启用多任务,减少I/O操作等待,而高效利用CPU。

现代操作系统对I/O的改进就是支持异步I/O,如果可以利用异步I/O,就可以用单线程实现多任务,Nginx就是支持异步I/O的Web服务器,它在单核CPU上利用单进程支持多任务,在多核CPU上可以运行多任务。

在python中,单线程+异步就是协程,单线程实现多任务。优势:1高效。子任务切换不是子线程切换,是由程序本身切换,没有开销。2不需要锁机制。只有一个线程,不存在同时写变量冲突,操作是只需要判断状态。

链接: [https://github.com/jackfrued/Python-100-Days/blob/master/Day01-15/13.%E8%BF%9B%E7%A8%8B%E5%92%8C%E7%BA%BF%E7%A8%8B.md]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值