21 python多线程-线程进程协程并发,锁,死锁[python基础]

为什么要学习多线程

同一时间做了很多事情。

使用场景

1,快速高效的爬虫程序
一个爬虫同时解析连接、爬取文字、爬取图片、代理IP验证码
2,多用户同时访问的web服务
3,电商秒杀、抢购活动
4,物联网传感器监控服务器

线程vs进程vs协程

关系:操作系统--(包含)--进程--(包含)--线程--(包含)--协程

重要性

1,跳槽、面试、决定薪资高度
2,解决效率问题
3,python的GIL导致的系列问题
4,通常会混合使用(多进程+协程)

进程

1,是一个执行中的程序。任务管理器中跑的应用。
2, 每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据。
3,操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。
4,进程也可通过派生(fork或spawn)新的进程来执行其他任务。

线程

1,在同一个进程下执行,并共享相同的上下文。
2,一个进程中的各个线程与主线程共享同一片数据空间。
3,线程包括开始、执行顺序和结束三部分。
4,它可以被抢占(中断)和临时挂起(也成为睡眠)——让步
5,线程一般是以并发方式执行。

并发

1,并发不能等同于并行处理。
2,它是一种属性—程序、算法或问题的属性。
3,并行只是并发问题的可能方法之一。
4,如果两个事件互不影响,则两个事件是并发的。

对多核的利用

1,单核CPU系统中,不存在真正的并发
2,GIL—全局解释器锁
3,GIL只是强制在任何时候只有一个线程可以执行python代码
4,I/O密集型(对磁盘读写密集)应用与CPU密集型(计算密集)的应用

GIL执行顺序

1,设置GIL
2,切换进一个线程去运行
3,执行下面操作之一:指定数量的字节码指令;线程主动让出控制权(可以调用time.sleep(0)来完成)
4,把线程设置回睡眠状态(切换出线程)
5,解锁GIL
6,重复上述步骤

实现一个线程(两种方法)

1,用threading模块代替thread模块
2,用threding.Tread创建线程
3,start()启动线程
4,join()挂起线程

threading模块的对象

Thread : 表示一个执行线程的对象.
Lock: 锁原语对象(和thread模块中的锁一样)
Rlock: 可重入锁对象,使单一线程可以(再次)获得已持有的所(递归锁)
Condition: 条件变量对象,使得一个线程等待另一个线程满足特定的条件,比如改变状态或某个数据值
Event:条件变量的通用版本,任意数量的线程等待某个事件的发生,在该事件发生后所有线程将被激活。
Semaphore:为线程间共享的有限资源提供了一个“计数器”,如果没有可用资源时会被阻塞。
BoundedSemaphore:与Semaphore相似,不过它不允许超过初始值。
Timer:与Thread相似,不过它要在运行前等待一段时间
Barrier:创建一个“障碍”,必须达到指定数量的线程后才可以继续。

Thread对象(实例化)数据属性

name:线程名
ident:线程的标识符
daemon:布尔标志,表示这个线程是否是守护线程(界面看不到,在后台跑)

Thread对象方法

init():实例化一个线程对象,需要有一个可调用的target,以及其参数args或kwargs。
start():开始执行该线程
run():定义线程功能的方法(通常在子类中被应用开发者重写)
join(timeout=None):直至启动的线程终止之前一直挂起;除非给出了timeout(秒),否则会一直阻塞。
getName():返回线程名
setName(name):设定线程名
isAlivel /is_alive():布尔标志,表示这个线程是否还存活
isDaemon():如果是守护线程,则返回True;否则,返回False
setDaemon():把线程的守护标志设定为布尔值daemonic(必须在线程start()之前调用)

实现一个线程

1,第一种方式
import threading
import time

def loop():
//心得线程执行的代码

n = 0
while n < 5:
    print(n)
    now_thread = threading.current_thread() //得到当前正在执行的线程
    print('[loop]now thread name:{0}'.format(now_thread.name))
    time.sleep(1)
    n += 1

def use_thread():
//使用线程来实现
now_thread = threading.current_thread()//取得当前在执行的线程
print('now thread name :{0}'.format(now_thread.name))
//设置线程
t = threading.Thread(target=loop, name='loop_thread')
//启动线程
t.start()
//挂起线程
t.join()

if name == 'main':
use_thread()

2,第二种方式
//定义类(继承)的方式

import threading
import time

class LoopThread(threading.Thread):
//自定义线程,继承自threading.Thread
n = 0
def run(self): //重写RUN()
while self.n < 5:
print(self.n)
now_thread = threading.current_thread()
print('[loop]now thread name:{0}'.format(now_thread.name))
time.sleep(1)
self.n += 1

if name == 'main':
//当前正在执行的线程名称
now_thread =threading.current_thread()
print('now thread name :{0}'.format(now_thread.name))
//设置线程
t = threading.Thread(target=loop, name='loop_thread')
//启动线程
t.start()
//挂起线程
t.join()

实现多个线程

1,最后为什么不是0?
// 多线程会共享上下文,当多线程操作一个全局变量时可能会出现问题。

import threading

//我的银行账户
balance = 0

def change_it(n):
//改变我的余额
global balance
balance = balance + n
balance = balance - n
print('--------------->{0}: balance--->{1}'.format(n, balance))

class ChangeBalanceThread(threading.Thread):

def __init__(self, num, *args, **kwargs)
    super().__init__(*args, **kwargs)
    self.num = num

def run(self, num, *args, **kwargs):
    for i in range(1000):
        change_it()

if name == 'main':
t1 = ChangeBalanceThread(5)
t2 = ChangeBalanceThread(8)
t1.start()
t2.start()
t1.join()
t2.join()
print('the last: {0}'.format(balance))

多线程中的锁实现

1,Lock()
2,Rlock()
3,Condition()

import threading
import time

//获得一把锁,Lock和RLock是两种不同类型的锁,RLock支持多次锁定
my_lock = threading.Lock()
your_lock = threading.RLock()

//我的银行账户
balance = 0

def change_it(n):
//改变我的余额
global balance

//第一种处理异常的方法
try:
//添加锁
print('start lock')
//第一种锁Lock,第二种锁是RLock
1,my_lock.acquire()
//2,your_lock.acquire()
//print('locked on')
///1,my_lock.acquire() //资源已经被锁住了,不能重复锁
定,产生四所
//2, your_lock.acquire() //RLock 支持多次锁定
print('locked two')
balance = balance + n
time.sleep(2)
balance = balance -n
time.sleep(1)
print('-N---> {0}; balance: {1}'.format(n, balance))
finally:
//释放掉锁
1,my_lock.release()
2,your_lock.release() // RLock支持多次锁定

//第二种处理异常的方法,with语法不用使用acquire()和release()了,更加简洁。
with your_lock:
balance = balance + n
time.sleep(2)
balance = balance -n
time.sleep(1)
print('-N---> {0}; balance: {1}'.format(n, balance))

class ChangeBalanceThread(threading.Thread):
//改变银行余额的线程
def init(self, num, *args, *kwargs):
super().init(
args, **kwargs)
self.num = num

def run(self):
    for i in range(100):
        change_it(self.num)

if name == 'main':
t1 = ChangeBalanceThread(5)
t2 = ChangeBalanceThread(8)
t1.start()
t2.start()
t1.join()
t1.join()
print('the last: {0}'.format(balance))

线程的调度和优化

import time
import threading
from multiprocessing.dummy import Pool

def run(n):
//线程要做的事情
time.sleep(2)
print(threading.current_thread().name, n)

def main():
//使用传统的方法来做任务
t1= time.time()
for n in range(100):
run(n)
print(time.time() - t1) //事件很长需要优化

def main_use_thread():
//使用线程优化任务
//资源有限,最多只能跑10个线程
t1= time.time()
ls = [ ]
for count in range(10): //每次开10个线程,开10次是100个线程。
for i in range(10):
t = threading.Thread(target=run, args=(i,))
ls.append(t)
t.start()

    for l in ls:
        l.join()
print(time.time() - t1)   

//上一个实现太麻烦,继续优化,使用线程池POOL
def main_use_pool():
//使用线程池来优化
t1 = time.time()
n_list = range(100)
pool = Pool(10)
pool.map(run, n_list)
pool.close()
pool.join()
print(time.time() - t1)

//用另一种性能更好的线程池

def main_use_executor():
//使用ThreadPoolExecutor来优化
t1 = time.time()
n_list = range(100)
with ThreadPoolExecutor(max_workers=10) as executor:
executor.map(run, n_list)
print(time.time() - t1)

if name == 'main':
//main()
// main_use_thread()
// main_use_pool()
main_use_executor()

进程

1,是一个执行中的程序
2,每个进程都拥有自己的地址空间、内存、数据栈以及其他用于跟踪执行的辅助数据(线程是共享上下文)
3,操作系统管理其上所有进程的执行,并为这些进程合理地分配时间。由操作系统管理
4,进程也可以通过派生(fork或spanwn)新的进程来执行其他任务

import time

进程的实现

def do_sth():
//进程要做的事情
print('进程的名称 {0}',pid: {1}.format(name, os.getpid))
time.sleep(5)
print('进程要做的事情')

//通过面向对象的方式实现
class MyProcess(Process):

def __init__(self, name, *args, **kwargs):  //把所有参数都加入
    self.name = name         //类中的方法run需要使用name,所以要重新构造方法 
    super().__init__(*args, **kwargs)  //这里会覆盖掉上一语句中的name属性

def run(self):
    print('进程的名称 {0}',pid: {1}.format(self.name, os.getpid))
    time.sleep(5)
    print('MyProcess进程要做的事情')

if name == 'main':
p = Process(target=do_sth, args=('my process', ) //用方法//args是一个元组
p = MyProcess('my process class') //面向对象的方式
//启动进程
p.start()
//挂起进程
p.join()

进程之间的通信

1,通过Queue、Pipes等实现进程之间的通信
from multiprocessing import Process, Queue, current_process
import random
import time

class WriteProcess(Proc ess):
//写的进程
def init(self, q, *args, *kwargs):
self.q = q
super().init(
args, **kwargs)

def run(self):
    //实现进程的业务逻辑
    //要写的内容
    ls = [ 
            "第1行内容",
            "第2行内容",
            "第3行内容",
            "第4行内容",
         ]
    for line in ls:
        print('写入内容:{0} -{1}'.format(line, currnt_process().name))
        self.q.put(line)
        //每写入一次,休息1-5秒
        time.sleep(random.randint(1,5))

class ReadProcess(Process):
//读取内容进程
def init(self, q, *args, *kwargs):
self.q = q
super().init(
args, **kwargs)

def run(self):
    while True:
        content = self.q.get()
        print('读取到的内容:{0}- {1}'.format(content), currnt_process().name)

if name == 'main':
//通过Queue共享数据
q =Queue()
//写入内容的进程
t_write = WriteProcess(q)
t_write.start()
//读取进程启动
t_read = ReadProcess(q)
t_read.start()

t_write.join()
t_read.join()
//因为读的进程是死循环,无法等待其结束,只能强制终止
t_read.terminate()

多进程中的锁

你可能感兴趣的:(21 python多线程-线程进程协程并发,锁,死锁[python基础])