0
点赞
收藏
分享

微信扫一扫

python网络编程—线程

扒皮狼 2022-01-10 阅读 76

1. 初识多线程

课前预习 

 

 

_thread(低级模块) 和 threading(高级模块,他是对_thraed的封装)

 

 

 

进程:数学题        线程:解题方法 

1.1 使用threading创建线程

threading提供了Thread(模块)这个方法创建一个线程对象

 

 

 

 

import threading,time  #threading即线程

#一个线程函数
def process():
    for i in range(3):
        time.sleep(1)
        print("线程的名字是:%s"%(threading.current_thread().name)) #threading.current_thread() 方法返回一个线程

if __name__ == '__main__':
    print("---主线程开始---")
    #创建4个线程,并且将其存入列表中
    thteads = [threading.Thread(target=process) for i in range(4)]  #threading.Thread(target=process) 创建线程
    #启动线程
    print("启动线程")
    for t in thteads:
        t.start()
    #结束线程
    print("结束线程")
    for t in thteads:
        t.join()
    print("---主线程结束---")

以上代码创建了4个线程,然后分别用for循环执行start()和join()方法,每个子线程分别执行输出三次。所以定义的process函数的作用就在于规范线程的执行状况。 

thteads = [threading.Thread(target=process) for i in range(4)]  

这一步中的 threading.Thread(target=process) 其实就是直接实例化一个线程对象,而threading.Thread即为创建线程的模块,相当于我们容易理解的:

t = threading.Thread(target=process) # t 就是实例化的线程对象  

类比进程中使用Process创建进程对象。

而对于函数:

#一个线程函数
def process():
    for i in range(3):
        time.sleep(1)
        print("线程的名字是:%s"%(threading.current_thread().name)) #threading.current_thread() 方法返回一个线程

就是编写自己对所创建的线程的要求和规范,作用类似于threading.Thread模块中的run()方法,所以下文中的通过继承threading.Thread类,创建SubThread子类,然后利用SubThread子类构建线程的方式,就省去了当前代码中再“特意”去编写一个def process函数规范线程的操作。(要求每个线程都执行3次) 

 

 

1.2 使用Thread子类创建线程 

 通过继承来创建线程,联系的进程中使用Process。

import threading,time

#继承threading.Thread模块,自定义一个线程子类
class SubThreads(threading.Thread):

    #重写threading.Thread中关于线程的运行方法run();使得每个线程执行三遍
    def run(self):
        for i in range(3):
            time.sleep(1) #休眠1s
            msg = ("子线程"+self.name+"现在执行第%s次"%str(i))
            print(msg)

if __name__ == '__main__':
    print("---主线程开始执行---")
    t1 = SubThreads() #实例化线程t1
    t2 = SubThreads()
    print("子线程开始启动...")
    t1.start() #启动子线程t1
    t2.start()
    t1.join() #等待子线程结束
    t2.join()
    print("---主线程结束---")

 如此便把1.1中def process的规范重写到run()方法中。

 2.线程间的通信

 

线程间的通信: 

from threading import Thread
import time

#规范一个子线程1
def pluse():
    print("---子线程1开始执行---")
    global g_num #定义一个全局变量
    g_num += 50
    print("执行子线程1,使g_num的值变为:%s"%(g_num))
    print("---子线程1结束---")

#规范一个子线程2
def minse():
    print("---子线程2开始执行---")
    global g_num #定义一个全局变量
    g_num += 50
    print("执行子线程2,使g_num的值变为:%s"%(g_num))
    print("---子线程2结束---")

g_num = 100 #定义一个全局变量
if __name__ == '__main__':
    print("---主线程启动---")
    print("初始时g_num的值为:%s"%(g_num))
    t1 = Thread(target=pluse) #实例化子线程1
    t2 = Thread(target=minse)
    print("---子线程启动---")
    t1.start()
    t2.start()
    t1.join()
    t2.join()
    print("---主线程结束---")

 

进程间的通信:

from multiprocessing import Process

#子进程1
def plus():
    print("---子进程1开始执行---")
    global g_num #声明全局变量
    g_num += 50
    print("在子进程1下:g_num = %d"%(g_num))
    print("---子进程1结束运行---")

#子进程2
def minus():
    print("---子进程2开始执行---")
    global g_num #声明全局变量
    g_num -= 50
    print("在子进程2下:g_num = %d"%(g_num))
    print("---子进程2结束运行---")

g_num = 100 #赋值全局变量
if __name__ == '__main__':
    print("---主进程启动---")
    print("在主进程运行中,g_num = %d"%(g_num))
    child1 = Process(target=plus) #实例化子进程1
    child2 = Process(target=minus)  # 实例化子进程2
    child1.start() #启动子进程1
    child2.start()

    child1.join() #等待子进程结束
    child2.join()
    print("---主进程结束---")

 对比如上两个程序可知,对于全局变量,子线程之间可以实现共享,而进程则不行。

 

 3. 什么是互斥锁

如上所诉,线程之间可以相互通信(共享全局变量),那么在执行多线程任务的时候,由于大家都可以共享和修改全局变量,就容易照成数据混乱,互斥锁就是解决这一问题而存在的。 

 例子:排队上卫生间,进去的人锁门,其他人排队等候。yue~

以电影院买票为例子:本场电影100张票 10个用户同时抢购电影票,每售出一张,显示剩余电影票。 

程序如下:

from threading import Thread,Lock #导入线程和互斥锁模块
import time

ticket_num = 100 #初始化100张电影票
mutex = Lock() #实例化一个互斥锁

#规定子线程的执行方式(此处模拟买电影票的过程)
def buy_ticket():
    global ticket_num #声明ticket_num为全局变量
    mutex.acquire() #锁定 
    temp = ticket_num
    time.sleep(0.2)
    ticket_num = temp - 1
    print("购票成功,剩余%s张电影票。"%(ticket_num))
    mutex.release() #释放锁

if __name__ == '__main__':
    list1 = [] #初始化一个列表,用于存放各个子线程   理解:“比如一个人买票的过程为一个子线程,那么n个人买票就是n个子线程,将这n个子线程存于列表中”
    for i in range(10): #10个人买票
        t = Thread(target=buy_ticket) #实例化子线程  (其实就是将买电影票过程实例化)
        list1.append(t) #将每一个人买票这个实例化对象存入列表中
        t.start() #启动线程
    for i in list1:
        t.join() #等待线程结束

 

假如没有互斥锁,即:

ticket_num = 100 #初始化100张电影票
#mutex = Lock() #实例化一个互斥锁

#规定子线程的执行方式(此处模拟买电影票的过程)
def buy_ticket():
    global ticket_num #声明ticket_num为全局变量
    #mutex.acquire() #锁定
    temp = ticket_num
    time.sleep(0.2)
    ticket_num = temp - 1
    print("购票成功,剩余%s张电影票。"%(ticket_num))
    #mutex.release() #释放锁

那么全局变量ticket_num 在初始化时(ticket_num =100时),就可以被存入列表List1中的10个所有子线程同时使用,那么打印的剩下的电影票数就都是“剩下99张”,明明出了10张,却剩下99,显然不对,这其实就是犯了10个人同时上1个卫生间的错误.....

 

再来理解一下互斥锁:

 

通过mutex.acquire()锁定,即子线程1在调用全局变量ticket_num时,可以有序进行。

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

举报

相关推荐

0 条评论