Python-线程

线程的概念

进程中一个分支逻辑的执行过程,一个进程中可以同时有多个分支,将这些分支称之为线程

所有的逻辑都需要在线程上运行

 

进程的概念

运行在操作系统上的程序就称之为进程

 

线程  ----> 进程     ----->  操作系统上

 

线程的目的:

多任务同时执行,且互不干扰,加快操作效率

如果没有多任务同时操作,则可能会出现:

例如:去火锅店吃火锅,问服务员什么时候可以点餐,服务员回答,不好意思,我正在服务另一桌,不能再服务你们

                   实际上,服务员本身并没有其他任务,只是单纯的等待那一桌吃完,此时其他可以帮助我们点餐,由于设计为单线程,因此无法同时工作

因此一般在一个程序中,会使用多线程来同时完成多个任务,以加快效率

 

线程的语法:

语法一:使用系统的线程类

# 语法 : threading.Thread(target, args)
    # target : 表示线程的目标逻辑,这个需要传入一个函数名称
        # 表示当前线程执行什么逻辑代码
    # args : 表示参数列表,之前设置的函数在执行过程中需要传入的参数
# 注意:这语法会返回创建出来的线程对象,需要将这个线程对象存储并使用


# 例如:
import threading

def dance():
    while True:
        print("正在唱歌......")

def sing():
    while True:
        print("-------正在跳舞")

if __name__ == "__main__":
    # 利用系统的线程的模块
    t1 = threading.Thread(target=dance)
    t2 = threading.Thread(target=sing)
    # 开启线程
    t1.start()
    t2.start()

现象:

解释: 为什么没有同时运行(运行有次数的差异)

原因:CPU分配时间片时是随机的,因此每条线程获取时间片的次数顺序无规律

 

线程的创建方法二:创建自己的线程类.

# 第一步:引入线程的模块
import threading
# 第二步:创建一个类别继承至系统的线程类
class MyThread(threading.Thread):

    def __init__(self, name):                   # 编写构造函数
        super().__init__(name=name)                  # 调用父类的构造函数给线程命名

    # 第三步:重写run函数(此函数为线程的主逻辑函数)
    def run(self):
        while True:
            print("{}正在跳舞......".format(self.name))


class Sing(threading.Thread):

    def __init__(self, name):                   # 编写构造函数
        super().__init__(name=name)                  # 调用父类的构造函数给线程命名

    # 第三步:重写run函数(此函数为线程的主逻辑函数)
    def run(self):
        while True:
            print("{}正在唱歌".format(self.name).rjust(20, "-"))

if __name__ == '__main__':
    t1 = MyThread('张三')
    t2 = Sing('李四')
    t1.start()
    t2.start()

练习:

有一个车站,一共有100张车票,现在有四个窗口同时售票,

每次出售后,输出显示:   XXX窗口出售了一张票,当前还剩下X张票

当票出售完毕后,程序结束

 

# 有一个车站,一共有100张票,分别有四个窗口同时进行售票
# 每次售票成功后,都输出显示信息: XXX出售了一张票,还剩下X张票
# 如果票出售完毕则结束

import threading


class Station:
    def __init__(self):
        # 创建一个变量来表示总票数
        self.ticket = 100

    # 编写一个函数用于出售车票
    def sell(self):
        if self.ticket > 0:
            self.ticket -= 1
            return True
        else:
            return False


class Seller(threading.Thread):
    def __init__(self, station, name):
        super().__init__(name=name)
        # 编写一个变量用于记录当前售票员在哪一个车站售票
        self.station = station

    def run(self):
        # 开启循环售票
        while True:
            result = self.station.sell()
            if result:
                print("{}成功出售了一张票,当前还剩下{}张票".format(self.name, self.station.ticket))
            else:
                print("已经没有票了")
                break

if __name__ == '__main__':
    # 创建车站对象
    station = Station()
    # 创建售票员(售票窗口)
    seller1 = Seller(station, '张三')
    seller2 = Seller(station, '李四')
    seller3 = Seller(station, '王五')
    seller4 = Seller(station, '赵六')
    # 开启售票
    seller1.start()
    seller2.start()
    seller3.start()
    seller4.start()








结果:由于CPU分配时间片时是随机分配,因此结果会出现小部分混乱

李四成功出售了一张票,当前还剩下15张票
王五成功出售了一张票,当前还剩下14张票
王五成功出售了一张票,当前还剩下13张票
赵六成功出售了一张票,当前还剩下12张票
赵六成功出售了一张票,当前还剩下11张票李四成功出售了一张票,当前还剩下9张票
李四成功出售了一张票,当前还剩下8张票
张三成功出售了一张票,当前还剩下10张票李四成功出售了一张票,当前还剩下7张票
张三成功出售了一张票,当前还剩下6张票
张三成功出售了一张票,当前还剩下5张票


王五成功出售了一张票,当前还剩下4张票李四成功出售了一张票,当前还剩下3张票
李四成功出售了一张票,当前还剩下2张票张三成功出售了一张票,当前还剩下1张票
王五成功出售了一张票,当前还剩下0张票
已经没有票了
已经没有票了

多线程共同访问一个变量的问题(共享数据问题)

当一个数据被多个线程共享时,由于线程的时间片原理,可能会导致数据的混乱(同时访问),此时的数据将不再准确

此时,就需要进行数据保护,Python提供了一个互斥锁的机制

类: threading.Lock

机制: 在锁范围的内的数据仅允许一条线程访问,当数据被访问时,其他线程在范围外等待,用此方式来保证数据的准确

语法:

例如:

import threading

number = 0

def func1():
    # 声明使用外部数据number(第三行的数据)
    global number
    for i in range(1000000):                # 循环一百万次
        number += 1                         # number的数据加一百万个1
    print("函数一的计算结果为:", number)

def func2():
    # 声明使用外部数据number(第三行的数据)
    global number
    for i in range(1000000):                # 循环一百万次
        number += 1                         # number的数据加一百万个1, number = number + 1
    print("函数二的计算结果为:", number)

if __name__ == '__main__':
    t1 = threading.Thread(target=func1)
    t2 = threading.Thread(target=func2)
    t1.start()
    t2.start()

预计结果:最后的数据应该是二百万,但是由于线程机制,结果是不固定,但大部分情况不够二百万

函数一的计算结果为: 1271354
函数二的计算结果为: 1374950

可以将数据操作设置一个互斥锁,使用互斥锁来进行数据的保护

操作分为三步:

# 第一步:获取锁对象
lock = threading.Lock()
# 第二步:开启互斥锁
lock.acquire()                      # 获取互斥锁(开启互斥效果)
# 第三步:释放互斥锁
lock.release()                      # 释放互斥锁(释放后,其他线程将可以访问这个数据)

例如:

def func1():
    # 声明使用外部数据number(第三行的数据)
    global number
    for i in range(1000000):                # 循环一百万次
        # 第二步:开启互斥锁
        lock.acquire()                      # 获取互斥锁(开启互斥效果)
        number += 1                         # number的数据加一百万个1
        # 第三步:释放互斥锁
        lock.release()                      # 释放互斥锁(释放后,其他线程将可以访问这个数据)
    print("函数一的计算结果为:", number)

结果为:

函数一的计算结果为: 1870502
函数二的计算结果为: 2000000

此时:最后的数据确实达到了预期效果,但是运行时间比之前更长(运行中出现了访问数据时等待时间),为了数据准确还是会加上互斥锁

全部代码:

import threading

number = 0

# 第一步:获取锁对象
lock = threading.Lock()

def func1():
    # 声明使用外部数据number(第三行的数据)
    global number
    for i in range(1000000):                # 循环一百万次
        # 第二步:开启互斥锁
        lock.acquire()                      # 获取互斥锁(开启互斥效果)
        number += 1                         # number的数据加一百万个1
        # 第三步:释放互斥锁
        lock.release()                      # 释放互斥锁(释放后,其他线程将可以访问这个数据)
    print("函数一的计算结果为:", number)

def func2():
    # 声明使用外部数据number(第三行的数据)
    global number
    for i in range(1000000):                # 循环一百万次
        # 第二步:开启互斥锁
        lock.acquire()  # 获取互斥锁(开启互斥效果)
        number += 1                         # number的数据加一百万个1, number = number + 1
        # 第三步:释放互斥锁
        lock.release()  # 释放互斥锁(释放后,其他线程将可以访问这个数据)
    print("函数二的计算结果为:", number)

if __name__ == '__main__':
    t1 = threading.Thread(target=func1)
    t2 = threading.Thread(target=func2)
    t1.start()
    t2.start()

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值