python之多线程和多进程编程

1.线程和进程概念

进程:是资源分配的最小单位,相当于公司老板,本身不干活但是为干活的员工提供资源和平台。

线程:是CPU调度的最小单位,相当于公司的员工,真正干活的人就是这些员工。

2.线程和进程的关系

1)一个线程只能属于一个进程,一个进程可以有多个进程,每个进程至少有一个线程(主线程)。

2)资源分配给进程,一个进程中的所有线程将共享该进程中所有资源。

3)线程在执行过程中,需要协作同步,不同进程间的线程需要利用消息同步机制来实现通讯。

4)真正执行任务的是线程,它是进程内可调度的实体。

3.线程和进程需要注意:

1)一个进程必须包含至少一个线程;

2)一个进程可以包含多个线程

3)不同进程间数据很难共享

4)同一进程下,不同线程间的数据则很容易共享

5)创建进程要比创建线程消耗更多的计算机资源

6)进程间不会相互影响,而相同进程内部如果有一个线程出现异常,则其他所有线程将全部挂掉。

7)同一进程内不同线程使用同一资源时需要加锁

4.比较重要的概念

1)串行:做完一件事再完后另一件事,完成任务是所有单个任务完后才能时间的总和。

2)并行:两个或多个事件在同一时刻发生,相当于多个人同时完成多件事,是物理意义上的并行。

3)并发:两个或多个事件在同一时间间隔内发生,相当于一个人同时做多件事,是逻辑意义上的并行。

5.线程相关的模块

_thread:这个模块是在python3之前thread模块的重命名,它是一个比较底层的模块,所以一般我们不会直接在代码中直接使用。

threading:python3之后的线程模块,编程中一般使用这个模块来创建线程。

创建线程的两种方式:一是通过继承于Thread类来创建新线程,二是通过Thread类构造器来创建新线程。

例子1:通过继承于Thread类来创建新线程,未等待子线程结束就直接执行了主线程

import threading
import urllib.request
import urllib.parse

def get_web(url):
    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
    headers={
        "User - Agent":user_agent
    }
    # 构造Request对象,以便我么设置http请求中的header
    req=urllib.request.Request('http://www.baidu.com',headers=headers)
    resp=urllib.request.urlopen(req)
    print(resp.read().decode()[:50])


if __name__ == '__main__':
    t1=threading.Thread(target=get_web,args=('https://www.baidu.com',))
    t2=threading.Thread(target=get_web,args=('http://www.woniuxy.com',))
    t1.start()#启动线程
    t2.start()
    print('程序运行结束!')

join方法:让子线程阻塞主线程的执行过程,也就是使用了join之后,主线程会等待使用了join方法的子线程结束后再往下执行。

例子2:等子线程结束后才会执行主线程

import threading
import urllib.request
import urllib.parse

def get_web(url):
    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
    headers={
        "User - Agent":user_agent
    }
    # 构造Request对象,以便我么设置http请求中的header
    req=urllib.request.Request('http://www.baidu.com',headers=headers)
    resp=urllib.request.urlopen(req)
    print(resp.read().decode()[:50])


if __name__ == '__main__':
    t1=threading.Thread(target=get_web,args=('https://www.baidu.com',))
    t2=threading.Thread(target=get_web,args=('http://www.woniuxy.com',))
    t1.start()#启动线程
    t2.start()
    t1.join()
    t2.join()
    print('程序运行结束!')

例子3:通过Thread类构造器来创建新线程,比较灵活

import threading
import urllib.request
import urllib.parse

class MyThread(threading.Thread):
    def __init__(self,name,url):
        super().__init__()#调用父类的初始化方法,保证父类初始化成功
        self.name= name
        self.url=url
    def run(self):#重写父类的run方法,定义在新的线程类里面要完成的task
        print('我是线程-{}'.format(self.name))
        user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
        headers = {
            "User - Agent": user_agent
        }
        # 构造Request对象,以便我么设置http请求中的header
        req = urllib.request.Request('http://www.baidu.com', headers=headers)
        resp = urllib.request.urlopen(req)
        print(resp.read().decode()[:50])

if __name__ == '__main__':
    t1=MyThread('线程1','https://www.baidu.com')
    t2=MyThread('线程2','http://www.woniuxy.com')
    t1.start()#启动线程
    t2.start()
    t1.join()
    t2.join()
    print('程序运行结束!')

6.锁的概念

锁GIL=全局解释器锁,他是粗粒度的锁,必须配置线程模块中的锁才能对每个原子操作进行锁定。

GIL全局解释器锁在什么时候会释放:

1)当当前的执行线程在执行IO操作时,会主动放弃 GIL;

2)当当前执行的线程执行了100条字节码的时候,会自动释放GIL锁;

例子1:

注意:获取锁和释放锁必须是成对出现的

import threading

num=0
lock=threading.Lock()
def deposite():
    for i in range(10000000):
        lock.acquire()#获取锁
        global num
        num +=1
        lock.release()#锁释放

def withdraw():
    for i in range(10000000):
        lock.acquire()  # 获取锁
        global num
        num -= 1
        lock.release()  # 锁释放

if __name__=='__main__':
    t1=threading.Thread(target=deposite)
    t2 = threading.Thread(target=withdraw)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(num)

例子2:lock.acquire()#获取锁和lock.release() # 锁释放==with lock

import threading

num=0
lock=threading.Lock()
def deposite():
    for i in range(10000000):
        # lock.acquire()#获取锁
        with lock:
            global num
            num +=1
        # lock.release()#锁释放

def withdraw():
    for i in range(10000000):
        # lock.acquire()  # 获取锁
        with lock:
            global num
            num -= 1
        # lock.release()  # 锁释放

if __name__=='__main__':
    t1=threading.Thread(target=deposite)
    t2 = threading.Thread(target=withdraw)
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print(num)

7.线程间通讯:

线程间通讯的几种方式:

Event:主要用于通过事件通知机制四线线程的大规模并发

Condition:主要用于多个线程间轮流交替执行任务

Queue:主要用于不通过线程间任务类型数据的共享

q=Queue(maxsize=0)

#阻塞程序,等待队列消息

q.get()

#阻塞程序,设置超时时间

q.get(timeout=1)

#发送消息,将消息放入队列

q.put()

例子1:模拟消息通讯的过程

from queue import Queue
import threading
import time

def product(q):
    kind=('aaaa','bbbb','cccc')
    for i in range(3):
        print(threading.current_thread().name,'生产的产品为。。。。')
        time.sleep(1)
        q.put(kind[i%3])
        print(threading.current_thread().name,'生产的产品已完成!')
def cousumer(q):
    while True:
        time.sleep(1)
        t=q.get()
        print('消费者消费的产品{}!'.format(t))



if __name__=='__main__':
    q=Queue(maxsize=1)
    #启动生产者线程
    threading.Thread(target=product,args=(q,)).start()
    threading.Thread(target=product, args=(q,)).start()
    #启动消费者线程
    threading.Thread(target=cousumer, args=(q,)).start()

8.创建多个进程

方式:一是一指定函数作为参数创建进程,二是继承Process类创建进程

例子1:指定函数作为参数创建进程

import urllib.request
import multiprocessing#引入多进程模块
import urllib.parse

def get_web(url):
    user_agent='Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
    headers={
        "User - Agent":user_agent
    }
    # 构造Request对象,以便我么设置http请求中的header
    req=urllib.request.Request('http://www.baidu.com',headers=headers)
    resp=urllib.request.urlopen(req)
    print(resp.read().decode()[:50])


if __name__=='__main__':
    p1=multiprocessing.Process(target=get_web,args=('https://www.baidu.com',))
    p2=multiprocessing.Process(target=get_web, args=('https://www.woniuxy.com',))
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('进程执行全部完成')

例子2:继承Process类创建进程

import urllib.request
import multiprocessing#引入多进程模块
import urllib.parse
import os


class MyProcess(multiprocessing.Process):
    def __init__(self,name,url):
        super().__init__()#调用父类的初始化方法,保证父类初始化成功
        self.name= name
        self.url=url
    def run(self):#重写父类的run方法,定义在新的进程类里面要完成的task
        print('我是进程-{}'.format(self.name),'当前进程ID为:',os.getpid(),'我的父进程ID为:',os.getppid())
        user_agent = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/115.0.0.0 Safari/537.36'
        headers = {
            "User - Agent": user_agent
        }
        # 构造Request对象,以便我么设置http请求中的header
        req = urllib.request.Request('http://www.baidu.com', headers=headers)
        resp = urllib.request.urlopen(req)
        print(resp.read().decode()[:50])


if __name__=='__main__':
    print('当前主进程ID为:',os.getpid())
    p1=MyProcess('子进程1','https://www.baidu.com')
    p2 = MyProcess('子进程1', 'https://www.woniuxy.com')
    p1.start()
    p2.start()
    p1.join()
    p2.join()
    print('进程执行全部完成')

9.多进程之间的通讯

例子1:设置的maxsize=1队列只能为1个

import multiprocessing
import time

def product(q):
    kind=('aa','bb','cc')
    for i in range(3):
        print(multiprocessing.current_process().name,'生产的产品为。。。。')
        time.sleep(1)
        q.put(kind[i%3])
        print(multiprocessing.current_process().name,'生产的产品已完成!')
def cousumer(q):
    while True:
        print(multiprocessing.current_process().name,'消费者准备。。。。')
        time.sleep(1)
        t=q.get()
        print('消费者消费的产品{}!'.format(t))



if __name__=='__main__':
    q=multiprocessing.Queue(maxsize=1)#创建多个进程队列对象,
    #启动生产者进程
    p1=multiprocessing.Process(target=product,args=(q,)).start()
    P2=multiprocessing.Process(target=product, args=(q,)).start()
    #启动消费者进程
    multiprocessing.Process(target=cousumer, args=(q,)).start()

10.多线程和多进程编程总结

多进程的优点:独立运行互不影响

多进程的缺点:创建进程的代价非常大

多线程的优点:效率比较高,不会耗费大量的资源

多线程的缺点:稳定性较差,一个奔溃后会影响整个进程。

多进程适用场景:对于计算密集型任务比较适合多进程

多线程适用场景:适合IO密集型任务,比如文件读取以及爬虫等操作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值