多线程简解

多任务操作机制的引入:在相同的硬件资源下提高任务的处理效率
在提升任务处理效率的基础上,提升用户体验
Python本身多任务处理机制
多线程任务处理机制(小民营企业式)
多进程任务处理机制(大型国企式)
协程多任务处理机制(家庭作坊式)

进程:计算机中一个程序在一个数据集上一次动态执行的过程
程序:描述进程的功能以及处理流程
数据集:功能处理过程中需要的资源数据
进程控制:严格控制进程执行过程中的各种状态(堵塞...等)

一个软件程序要运行,需要将软件依赖的数据加载到内存之中,通过CPU进行运算,并按照程序定义的逻辑进行流程控制,直到数据处理完成之后程序退出
在程序执行的过程中,进程只是负责分配需要的资源数据,是程序的主体
程序运行过程中真正运行的是线程
每个进程中至少有一个线程(主线程)

进程主要是用来分配任务,真正干活的是线程

线程:计算机中程序运行的实际执行者,又称轻量级线程,是一个CPU的执行单元。
每个进程中至少有一个线程(主线程)
一个线程只能属于一个进程
一个进程可以有多个线程,多个线程之间可以共享进程中提供的数据
CPU运算分配给线程,CPU 上运算执行的就是线程
线程是最小的运行单元, 进程是最小的资源管理单元

形象化:
进程:公司老板,提供资源给线程使用
主线程:管理人员/干活
子线程:干活的人

串行:传统意义上的同步,顺序的意思,按照一定的执行步骤顺序执行每个环节
并行:传统意义上的异步,同时的意思,多个任务同时执行,同时执行接收到的多个任务
并发:同时接收到多个任务,同时执行多个任务,但是具体到某个时刻的时候只在执行其中 一个任务,只不过是在很短的时间内在多个任务之间切换,模拟形成了多个任务在同 时执行的现象

Python中支持并行操作,但为了保证多任务机制下的共享数据的安全,python内置了一个GIL(全局解释器)只允许同一时间内CPU只能执行一个线程。所以在python的官方解释器下,多线程是多线程并发机制

Python中多线程并发机制
提供了两种模块:_thread 和 threading
_thread 适合用于大神
threading 适合新手使用(官方推荐)

_thread 的使用:

#引入需要的模块
import _thread, time


#定义第一个函数
def sing():
    for i in range(20):
        print("篝火盛宴!!!")


#定义第二个函数
def info():
    for i in range(20):
        print("载歌载舞!!!")


#创建多个线程
_thread.start_new_thread(sing, ())
_thread.start_new_thread(info, ())

#让系统主线程等待子线程运行结束
time.sleep(3)

threading 的使用:
可以实现面向过程的并发编程
也可以实现面向对象的并发编程

常用的threading 模块属性和方法
Thread 线程类,用于创建和管理线程
Event 事件类,用于作线程同步 添加一个标志,通过标志进行等待,运 行,清除
Condition 条件类,用于作线程同步 可以实现加锁,开锁,等待,运行等
Lock/RLock 锁类,用于线程同步 加锁/开锁
Timer 延时线程,用于在一定事件后在执行一个异步函数

常见的Thread 模块属性和方法
init(name线程名称,target面向过程开发时指定执行的函数,args面向过程开发时指定给函数的参数) 构建方法,构建线程类型
run() 面向过程开发时,需要重写的方法,线程一旦启动,会自动执行
start() 线程启动的方法,会创建一个新的线程,并自动调用run方法
join 独占模式
deamon 守护线程

基于threading模块的多线程并发编程(火车票售票窗口)

#引入需要的模块
import threading, time

#定义车票
che_piao = 10

#定义线程锁对象
lock = threading.Lock()

#定义售票函数
def shou_piao():

    global che_piao

    while True:
        time.sleep(0.5)

        #线程锁上锁
        if lock.acquire():
            if che_piao > 0:
                che_piao -= 1
                print("售出一张车票")

            else:
                print("本车次票已售完")
                # 线程锁解锁
                lock.release()
                break
            lock.release()

if __name__ == "__main__":
    # #单线程售票系统
    # shou_piao()

    #多线程售票系统
    for i in range(5):
        t = threading.Thread(name = "窗口"+str(i), target=shou_piao)
        t.start()

# #定义一个售票窗口
# t1 = threading.Thread(name='一号售票窗口', target=shou_piao)
#
# #启动代码
# t1.start()

线程管理——锁(Lock/RLock)
多线程程序在运行过程中,由于多个线程访问的时同一部分数据,很容易会造成共享数据访问冲突的现象,如果一旦出现冲突程序就会出现与执行结果不符合期望的结果
python 提供了两种线程锁的操作
同步锁/互斥锁: Lock
可重用锁: RLock
锁的操作
acquire(), 获取锁,上锁
Release(), 释放锁,解锁

#引入需要的模块
import threading, time

#定义车票
che_piao = 10

#定义线程锁对象
lock = threading.Lock()

#定义售票函数
def shou_piao():

    global che_piao

    while True:
        time.sleep(0.5)

        #线程锁上锁
        if lock.acquire():
            if che_piao > 0:
                che_piao -= 1
                print("售出一张车票")

            else:
                print("本车次票已售完")
                # 线程锁解锁
                lock.release()
                break
            lock.release()

if __name__ == "__main__":
    # #单线程售票系统
    # shou_piao()

    #多线程售票系统
    for i in range(5):
        t = threading.Thread(name = "窗口"+str(i), target=shou_piao)
        t.start()

# #定义一个售票窗口
# t1 = threading.Thread(name='一号售票窗口', target=shou_piao)
#
# #启动代码
# t1.start()

线程管理——死锁(哲学家吃饭问题)
线程锁固然功能强大,可以管理多个线程之间的共享数据问题 但是同时它的强大也带来了比较纠结的问题,需要开发人员对于锁定的数据有一个良好的认知,否则特别容易造成死锁的现象,比较著名的哲学家吃饭问题就是死锁的典型代表

由于计算机运算速度较快,所以有两种方案可以将问题放大
给执行函数添加休眠时间
添加线程数量
死锁并不是每次都会出现的,而是程序在执行过程中,根据系统 CPU 时间片的切换机制恰 好遇到了重复上锁的情况,就会死锁
死锁情况可以用重用锁 RLock 进行锁定处理

#引入需要的模块
import threading

#定义两把锁
lock_a = threading.Lock()
lock_b = threading.Lock()


def ZhexueA():
    for i in range(200):
        if lock_a.acquire():
            if lock_b.acquire():
                print("你给我刀子,我给你叉子")
                lock_b.release()
            lock_a.release()

def ZhexueB():
    for j in range(200):
        if lock_b.acquire():
            if  lock_a.acquire():
                print("你给我叉子, 我给你刀子")
                lock_a.release()
            lock_b.release()

if __name__ == "__main__":

    bj = threading.Thread(target=ZhexueA)
    bj.start()
    ss = threading.Thread(target=ZhexueB)
    ss.start()

线程管理——事件
针对多个线程之间的通信的问题,python提供了Event进行处理
Event.set() 添加一个标记状态
Event.clear() 清除标记状态
Event.wait() 事件对象操作的当前线程等待,直到该对象被标记状态

Event实现

"""
小贩:生产油条
顾客:消费油条
需求:顾客消费

两个线程之间的通信:事件对象, threading.Event
event.set()  添加标记
event.wait()  线程等待
event.clear()
"""

#引入需要的模块
import threading,time

#定义一个事件对象
event = threading.Event()

#定义商贩
def shang_fan():
    print("商贩:铁锅油条")
    time.sleep(2)
    #添加标记
    event.set()
    #清除标记
    event.clear()

    print("商贩:刚出锅的")
    #线程等待
    event.wait()

    print("商贩:谢谢光临")


#定义顾客
def gu_ke():
    #线程等待
    event.wait()
    print("顾客:老板,两根油条")

    time.sleep(2)
    print("顾客:外焦里嫩")

    print("顾客:下次还来")
    #添加标记
    event.set()

if __name__ == "__main__":
    sf = threading.Thread(target=shang_fan)
    gk = threading.Thread(target=gu_ke)

    sf.start()
    gk.start()

线程管理——条件
线程条件 Condition 对象,也是多线程并发模式下一种线程之间的通信
Condition.acquire() 锁定
Condition.release() 解锁
Condition.wait() 释放锁
Condition.notify() 唤醒

Condition 的实现

#引入需要的对象
import threading, time, random

#创建一个条件对象
con = threading.Condition()

#创建篮子
basket = list()

#生产者
def product():
    while True:
        time.sleep(1)
        #上锁
        con.acquire()

        if len(basket) > 20:
            print(threading.current_thread().getName(), ":篮子满了,快来吃包子吧")
            con.wait()
        else:
            #生产一个包子
            _no = random.randint(1,10)
            print(threading.current_thread().getName(), ":蒸好了一个包子", _no)
            basket.append(_no)
            con.notify()

        #解锁
        con.release()


def consumer():
    while True:
        time.sleep(0.5)
        #上锁
        con.acquire()

        if len(basket) <= 0:
            print(threading.current_thread().getName(), ":吃光了,快点去蒸包子吧")
            con.wait()
        else:
            _no = basket.pop()
            print(threading.current_thread().getName(), ":吃了一个包子", _no)
            con.notify()

        #解锁
        con.release()


if __name__ == "__main__":
    #生产厂家
    for i in range(5):
        p = threading.Thread(name="生产者" + str(i) + "号", target=product)
        p.start()

    #消费人群
    for j in range(5):
        c = threading.Thread(name="消费者" + str(j) + "号", target=consumer)
        c.start()

线程管理——队列
多线程并发编程的重点,是线程之间共享数据的访问问题和线程之间的通信问题 为了解决线程之间数据共享问题,PYTHON 提供了一个数据类型【队列】可以用于在多线程 并发模式下,安全的访问数据而不会造成数据共享冲突
put([timeout=None]) 向队列中添加数据,队列如果满了,一直阻塞直到超时或者队 列中有数据被删除之后添加成功
get([timeout=None]) 从队列中获取数据,如果队列为空,一直阻塞直到超时或者队 列中添加数据之后获取成功
队列实现Queue:

#引入需要的模块
import threading, queue, time, random

# #创建一个条件对象
# con = threading.Condition()

#定义一个队列储存数据
basket = queue.Queue(20)

def product():

    while True:
        time.sleep(1)
        _no = random.randint(0, 10)
        try:
            basket.put(_no, timeout=1)
            print("生产者生产了一个数据", _no)
        except:
            print("篮子满了")


def consumer():

    while True:
        time.sleep(0.5)
        try:
            _no = basket.get(timeout = 1)
            print("消费者取走了一个数据", _no)
        except:
            print("篮子空了.................")


if __name__ == "__main__":
    for i in range(2):
        p = threading.Thread(target=product)
        p.start()

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

推荐阅读更多精彩内容

  • 1.进程和线程 1.1系统多任务机制 多任务操作机制的引入主要是在相同的硬件资源下怎么提高任务处理效率的!多任务的...
    _宁采臣阅读 965评论 0 6
  • Java-Review-Note——4.多线程 标签: JavaStudy PS:本来是分开三篇的,后来想想还是整...
    coder_pig阅读 1,629评论 2 17
  • Object C中创建线程的方法是什么?如果在主线程中执行代码,方法是什么?如果想延时执行代码、方法又是什么? 1...
    AlanGe阅读 1,716评论 0 17
  • 历史总是一次一次又一次的重演,不是那种大人物的历史,我这个小人物给我写点开心的故事吧,让我少历点劫
    糖馒头阅读 145评论 0 0
  • 一见钟情,再念方知已倾心。繁华盛世不得你,伊携红衣,飘然入梦矣。 世事多可恼。且与自抒怀,有清风徐来,...
    小守拙阅读 517评论 0 1