多线程简解

作者: 言烬 | 来源:发表于2018-08-20 19:47 被阅读0次

多任务操作机制的引入:在相同的硬件资源下提高任务的处理效率
在提升任务处理效率的基础上,提升用户体验
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()

相关文章

  • 多线程简解

    多任务操作机制的引入:在相同的硬件资源下提高任务的处理效率在提升任务处理效率的基础上,提升用户体验Python本身...

  • 多线程

    参考文章:iOS多线程--彻底学会多线程之『GCD』Swift 3.0 GCD和DispatchQueue 使用解...

  • iOS开发多线程篇-NSThread

    上篇我们学习了iOS多线程解决方式中的NSOperation,这篇我主要概况总结iOS多线程中NSThread的解...

  • iOS 多线程理解 - GCD篇

    提及多线程 , 我们一定不会陌生 , 甚至线程问题已经成为iOS 面试必问类型.iOS 官方提供了 几种多线程的解...

  • iOS 多线程基础

    转自:iOS 多线程基础 - 简书 多线程同步和异步的区别?IOS中如何实现多线程的同步? 异步:可以同时执行多条...

  • 多线程异步任务处理

    @(多线程&&并发) 多线程异步任务处理 欢迎关注作者简书csdn传送门   我们常用ThreadPoolExec...

  • 关于线程

    多线程与线程优化CPU核心数与线程数的关系多核心多线程 Simultaneous Multithreading.简...

  • springboot开启多线程定时任务

    spring-boot | 多线程并发定时任务 - 简书

  • 解决异常:com.mongodb.MongoSocketRead

    1 版本 mongo-java-driver3.12.0 2场景 多线程同时读取和写入mongodb 3异常 4解...

  • startActivity简解

    1.概要 startActivity启动一个activity的源码分析,涉及概念较多,比如binder,aidl跨...

网友评论

    本文标题:多线程简解

    本文链接:https://www.haomeiwen.com/subject/alxriftx.html