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()

多进程中的锁

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容

  • 线程 操作系统线程理论 线程概念的引入背景 进程 之前我们已经了解了操作系统中进程的概念,程序并不能单独运行,只有...
    go以恒阅读 1,630评论 0 6
  • 多进程 要让python程序实现多进程,我们先了解操作系统的相关知识。 Unix、Linux操作系统提供了一个fo...
    蓓蓓的万能男友阅读 590评论 0 1
  • 一文读懂Python多线程 1、线程和进程 计算机的核心是CPU,它承担了所有的计算任务。它就像一座工厂,时刻在运...
    星丶雲阅读 1,442评论 0 4
  • 环境 xubuntu anaconda pycharm python https://www.cnblogs.co...
    Ericoool阅读 1,890评论 0 0
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,729评论 0 8