python 线程、进程与协程

一、什么是线程?什么是进程?

第一,进程是一个实体。每一个进程都有它自己的地址空间,一般情况下,包括文本区域(text region)、数据区域(data region)和堆栈(stack region)。文本区域存储处理器执行的代码;数据区域存储变量和进程执行期间使用的动态分配的内存;堆栈区域存储着活动过程调用的指令和本地变量。
第二,进程是一个“执行中的程序”。程序是一个没有生命的实体,只有处理器赋予程序生命时(操作系统执行之),它才能成为一个活动的实体,我们称其为进程。[3] 
进程是操作系统中最基本、重要的概念。是多道程序系统出现后,为了刻画系统内部出现的动态情况,描述系统内部各道程序的活动规律引进的一个概念,所有多道程序设计操作系统都建立在进程的基础上。

进程的概念
进程的定义
线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。

在单个程序中同时运行多个线程完成不同的工作,称为多线程。
线程定义

注意:

  对于python而言,其自己没有进程和线程,在执行的时候需要调用操作系统的进程和线程。

二、应用软件、进程和线程的关系

        程序和进程的区别就在于:程序是指令的集合,它是进程运行的静态描述文本;进程是程序的一次执行活动,属于动态概念。在多道编程中,我们允许多个程序同时加载到内存中,在操作系统的调度下,可以实现并发地执行。这是这样的设计,大大提高了CPU的利用率。进程的出现让每个用户感觉到自己独享CPU,因此,进程就是为了在CPU上实现多道编程而提出的。

  简单来说:一个应用程序(软件),可以有多个进程(默认只有一个),一个进程中可以创建多个线程(默认一个)。

三、进程与线程的区别

第一:
进程是cpu资源分配的最小单元。
线程是cpu计算的最小单元。
第二:
一个进程中可以有多个线程。
第三:
对于Python来说他的进程和线程和其他语言有差异,是有GIL锁。
GIL锁保证一个进程中同一时刻只有一个线程被cpu调度。

注意:IO密集型操作可以使用多线程;计算密集型可以使用多进程;
*****通过漫画了解进程与线程

四、线程

  (一)线程的基本使用

#主程序默认等待子程序执行完毕
import threading
import time
def func(arg):
    time.sleep(arg)
    print(arg)


t1 = threading.Thread(target=func,args=(3,))
t1.start()

t2 = threading.Thread(target=func,args=(2,))
t2.start()

print(123)

  (二)线程的常用方法

  1、start     线程准备就绪,等待CPU调度

  2、set daemon  主程序不再等,主程序终止则所有子程序终止

import threading
import time
def func(arg):
    time.sleep(2)
    print(arg)

t1 = threading.Thread(target=func,args=(3,))
t1.setDaemon(True)   #设置主程序不在等待子程序执行完毕,主程序终止则子程序终止
t1.start()

t2 = threading.Thread(target=func,args=(9,))
t2.setDaemon(True)
t2.start()

print(123)

  3、join  开发者可以控制主线程等待子线程(最多等待时间)

import threading
import time
def func(arg):
    time.sleep(arg)
    print(arg)

print('创建子线程t1')
t1 = threading.Thread(target=func,args=(3,))
t1.start()
# join()无参数,让主线程在这里等着,等到子线程t1执行完毕,才可以继续往下走。
# join(n)有参数,让主线程在这里最多等待n秒,无论是否执行完毕,会继续往下走,但最终会等所有子程序执行完毕主程序在终止。
t1.join(6)

print('创建子线程t2')
t2 = threading.Thread(target=func,args=(6,))
t2.start()
t2.join(6) 

print(123)

  4、其它方法

import threading
import time
def func(arg):
    # 获取当前执行该函数的线程的对象
    t = threading.current_thread()
    # 根据当前线程对象获取当前线程名称
    name = t.getName()
    print(name,arg)

t1 = threading.Thread(target=func,args=(11,))
t1.setName('线程1')     #设置线程名称
t1.start()

t2 = threading.Thread(target=func,args=(22,))
t2.setName('线程2')    #设置线程名称
t2.start()

print(123)

  (三)两种方法创建多线程

  #多线程方式一(常见):

import threading
def func(arg):
    print(arg)

for i in range(4):
    t = threading.Thread(target=func,args=(i,))
    t.start()
View Code

  #多线程方式二(通过面向对象创建):

import threading
class MyThread(threading.Thread):

    def run(self):
        print(11111,self._args,self._kwargs)

t1 = MyThread(args=(11,))
t1.start()

t2 = MyThread(args=(22,))
t2.start()
View Code

  (四)几种常见的线程锁

  问题:为什么要加线程锁?

  #如果不加锁就可能出现多个线程抢占资源的情况,从而出现脏数据,加上锁之后可以保证在同一时刻只有一个线程在执行。

  *****同步锁******

import threading
import time
lock = threading.Lock()
v = []
def task(i):
    lock.acquire()
    v.append(i)
    time.sleep(0.01)
    m = v[-1]
    print(m,i)
    lock.release()
for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()
Lock

  *****递归锁:可以多次获得锁,多次释放锁******

import threading
import time
lock = threading.RLock()
v = []
def task(i):
    lock.acquire()
    lock.acquire()
    v.append(i)
    time.sleep(0.01)
    m = v[-1]
    print(m,i)
    lock.release()
    lock.release()
for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()
RLock

  *****信号锁:可以控制一次最多有n个线程执行代码******

import threading
import time
lock = threading.BoundedSemaphore(5)
def task(i):
    lock.acquire()
    print(i)
    time.sleep(1)
    lock.release()
for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()
BoundedSemaphore

  *****条件锁:简单理解,满足条件时,释放线程,根据输入数量释放相应的线程数量******

import threading
import time
lock = threading.Condition()
def task(i):
    print('线程来了',i)
    lock.acquire()
    lock.wait()
    print(i)
    time.sleep(1)
    lock.release()

for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()
while 1:
    num = int(input('请输入一次要执行的线程数量>>>'))
    lock.acquire()
    lock.notify(num)
    lock.release()
Condition

  *****事件锁:一次放所有线程******

import threading
import time
lock = threading.Event()
def task(i):
    print('线程来了')
    lock.wait()    #“Flag”值为False
    print(i)
    time.sleep(1)
for i in range(10):
    t = threading.Thread(target=task,args=(i,))
    t.start()    
input('>>>')
lock.set()     #“Flag”值为True
lock.clear     #“Flag”值为False
Event

  (五)线程池

from concurrent.futures import ThreadPoolExecutor
import time

def task(a1,a2):
    time.sleep(2)
    print(a1,a2)

# 创建了一个线程池(最多5个线程)
pool = ThreadPoolExecutor(5)

for i in range(40):
    # 去线程池中申请一个线程,让线程执行task函数。
    pool.submit(task,i,8)
View Code

  (六)生产者消费者模型

import time
import queue
import threading
q = queue.Queue() # 线程安全

def producer(id):
    """
    生产者
    :return:
    """
    while True:
        time.sleep(2)
        q.put('包子')
        print('厨师%s 生产了一个包子' %id )

for i in range(1,4):
    t = threading.Thread(target=producer,args=(i,))
    t.start()

def consumer(id):
    """
    消费者
    :return:
    """
    while True:
        time.sleep(1)
        v1 = q.get()
        print('顾客 %s 吃了一个包子' % id)

for i in range(1,3):
    t = threading.Thread(target=consumer,args=(i,))
    t.start()
View Code

  (七)threadinglocal 原理

import time
import threading
INFO = {}
class Local(object):

    def __getattr__(self, item):
        ident = threading.get_ident()
        return INFO[ident][item]

    def __setattr__(self, key, value):
        ident = threading.get_ident()
        if ident in INFO:
            INFO[ident][key] = value
        else:
            INFO[ident] = {key:value}

obj = Local()
def func(arg):
    obj.phone = arg # 调用对象的 __setattr__方法(“phone”,1)
    time.sleep(2)
    print(obj.phone,arg)

for i in range(10):
    t =threading.Thread(target=func,args=(i,))
    t.start()
易理解版本
import time
import threading

class Local(object):
    def __init__(self):
        object.__setattr__(self,'INFO',{})
    def __getattr__(self, item):
        ident = threading.get_ident()
        return self.INFO[ident][item]

    def __setattr__(self, key, value):
        ident = threading.get_ident()
        if ident in self.INFO:
            self.INFO[ident][key] = value
        else:
            self.INFO[ident] = {key:value}

obj = Local()
def func(arg):
    obj.phone = arg # 调用对象的 __setattr__方法(“phone”,1)
    time.sleep(2)
    print(obj.phone,arg)

for i in range(10):
    t =threading.Thread(target=func,args=(i,))
    t.start()
改装版

五、进程

  (一)进程的常用功能----------------其方法和线程类似

- join
- daemon
- name
- multiprocessing.current_process()
- multiprocessing.current_process().ident/pid
import time
import multiprocessing
def task(arg):
    # time.sleep(3)
    print(arg)
    p = multiprocessing.current_process()    #获取当前工作进程
    print(p.name)      #获取进程名称

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=task,args=(1,))
    p1.name = 'pp1'     #设置进程名称
    p1.daemon=True     #主进程执行完毕所有子进程不论执行到哪里都关闭
    p1.start()
    # p1.join(0.5)     #加参数主进程最长等待子进程0.5秒就向下执行,但最终都会等子进程执行完毕在关闭

    print(1234)

    p2 = multiprocessing.Process(target=task, args=(2,))
    p2.name = 'pp2'     #设置进程名称
    p2.daemon=True
    p2.start()
    # p2.join(0.5)
    print(2345)
进程常用方法

  (二)进程的数据共享问题

  进程之间的数据默认不共享,但通过Queue和Manager可以使进程之间的数据共享

import multiprocessing
data_list = []
def task(arg):
    data_list.append(arg)
    print(data_list)

def run():
    for i in range(4):
        p = multiprocessing.Process(target=task,args=(i,))
        p.start()

if __name__ == '__main__':
    run()
#结果
# [2]
# [0]
# [1]
# [3]
数据不共享

  *******进程间的数据共享:multiprocessing.Queue

q = multiprocessing.Queue()

def task(arg,q):
    q.put(arg)

def run():
    for i in range(10):
        p = multiprocessing.Process(target=task, args=(i, q,))
        p.start()
    while True:
        v = q.get()
        print(v)
if __name__ == '__main__':
    run()

  **********进程间的数据共享:Manager

def task(arg,dic):
    time.sleep(2)
    dic[arg] = 100

if __name__ == '__main__':
    m = multiprocessing.Manager()
    dic = m.dict()
    process_list = []
    for i in range(10):
        p = multiprocessing.Process(target=task, args=(i,dic,))
        p.start()
        process_list.append(p)

    #处理主进程和子进程数据共享问题,避免主进程结束之后共享的数据消失
    while True:
        count = 0
        for p in process_list:
            if not p.is_alive():
                count += 1
        if count == len(process_list):
            break
    print(dic)

  (三)进程锁

  参考线程锁,其使用方法及其类似

  *******为什么会有进程锁?

    当多个进程共享某个数据时,为了保证数据安全,不出现脏数据,会给进程加锁.

  (四)进程池

import time
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor

def task(arg):
    time.sleep(2)
    print(arg)

if __name__ == '__main__':
    pool = ProcessPoolExecutor(5)   #进程池里面设置了5个进程
    for i in range(10):
        pool.submit(task,i)

六、简单爬虫实例--------使用线程池

import requests
from bs4 import BeautifulSoup
from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor


# 模拟浏览器发送请求
# 内部创建 sk = socket.socket()
# 和抽屉进行socket连接 sk.connect(...)
# sk.sendall('...')
# sk.recv(...)

def task(url):
    r1 = requests.get(
        url=url,
        headers={
            'User-Agent':'Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.92 Safari/537.36'
        }
    )

    # 查看下载下来的文本信息
    soup = BeautifulSoup(r1.text,'html.parser')
    content_list = soup.find('div',attrs={'id':'content-list'})
    for item in content_list.find_all('div',attrs={'class':'item'}):
        title = item.find('a').text.strip()
        target_url = item.find('a').get('href')
        print(title,target_url)

def run():
    pool = ThreadPoolExecutor(5)
    for i in range(1,50):
        pool.submit(task,'https://dig.chouti.com/all/hot/recent/%s' %i)


if __name__ == '__main__':
    run()
View Code

猜你喜欢

转载自www.cnblogs.com/fengchong/p/9637221.html