python线程

本文介绍了Python线程的基础知识,包括线程的概念、并发与并行的区别,并通过案例代码展示了线程的创建与使用,包括共享全局变量、线程同步(互斥锁)以及如何避免死锁。此外,还提到了一个使用多线程实现的UDP聊天器应用。
摘要由CSDN通过智能技术生成

一、基础知识

\qquad 线程是程序中一个单一的顺序控制流程。进程内有一个相对独立的、可调度的执行单元,是系统独立调度和分派CPU的基本单位指令运行时的程序的调度单位。在单个程序中同时运行多个线程完成不同的工作,称为多线程。
\qquad 单核CPU在同一时刻只能运行一个线程,电脑上能同时运行多个程序,是因为CPU在运行的时候,CPU随机选取一个程序运行,然后在随机选取另一个程序运行,由于CPU每次选取程序运行的时间极短,所以认为感觉到的是多个程序一起运行。
\qquad 当任务数大于CPU核数时成为并发,当任务数大于或者等于CPU核数时,称为并行。

二、案例代码

\qquad 1) 编写程序打印5行1,5行2

import time

def test1():
    for i in range(5):
        print("---1---")
        time.sleep(1)

def test2():
    for i in range(5):
        print("---2---")
        time.sleep(1)
        
def main():
    test1()
    test2()

if __name__ == "__main__":
    main()

\qquad 上述代码运行的结果是打印一行“—1---”后等1秒,再打印一行"—1---",这样打印5行"—1---“后,等1秒后继续打印”—2---",再等1秒钟,继续打印"—2---",直至打印5行"—2---",整个过程总共用时10秒。

\qquad 2) 编写程序,同时打印一行1和一行2,打印五次。

import time
import threading

def test1():
    for i in range(5):
        print("---1---")
        time.sleep(1)

def test2():
    for i in range(5):
        print("---2---")
        time.sleep(1)
        
def main():
    #这里只生成了一个普通的对象,未生成子线程
    t1 = threading.Thread(target=test1)
    t2 = threading.Thread(target=test2)
    #这里生成一个子线程,开始调用运行test1函数
    t1.start()
    #如果要让test1()先执行,然后test2()在执行可在t1生成子线程后延时
    #time.sleep(1)
    t2.start()

if __name__ == "__main__":
    main()

\qquad 上述代码运行结果:打印一行"—1---",一行"—2---“后等1秒,继续打印一行”—1---",一行"—2---",直至程序结束,总共花时5秒。

\qquad 3) thread函数args参数的使用

import threading
import time

def test1(num1):
    for i in range(num1):
        print("---%d---" % i)
        time.sleep(1)
        
def test2(num2):
    for i in range(num2):
        print("---%d---" % i)
        time.sleep(1)
        
def main():
    #当子线程指向的函数有形参时,threading类的Thread函数里的参数可以传递参数,需要注意的是args后面的值必须是元组
    t1 = threading.Thread(target=test1,args=(5,))
    t2 = threading.Thread(target=test2,args=(10,))
    t1.start()
    t2.start()
    while True:
        #循环打印当前线程
        print(threading.enumerate())
        time.sleep(1)
        #当生成的两个子线程结束后,使得循环结束
        if len(threading.enumerate())<=5:
            break
    
    
if __name__ == "__main__":
    main()

\qquad 4) 验证线程共享全局变量

import threading
import time

a=0

def test1(num1):
    global a
    for i in range(num1):
        a=a+1
        
def test2(num2):
    global a
    for i in range(num2):
        a=a+1
        
def main():
    t1 = threading.Thread(target=test1,args=(100,))
    t2 = threading.Thread(target=test2,args=(100,))
    t1.start()
    t2.start()
    print(a)
    
if __name__ == "__main__":
    main()

\qquad 代码解释:若最后的打印出a的值为200,说明多线程是共享全局变量的,若最后打印出a的值不等于200,则说明不是共享全局变量的。
\qquad 代码结果:最后打印出a的值为200

\qquad 5)验证共享全局变量的弊端

import threading
import time

a=0

def test1(num1):
    global a
    for i in range(num1):
        a=a+1
    print("--test1---%d---\n" % a)
        
def test2(num2):
    global a
    for i in range(num2):
        a=a+1
    print("--test2---%d---\n" % a)  
        
def main():
    t1 = threading.Thread(target=test1,args=(1000000,))
    t2 = threading.Thread(target=test2,args=(1000000,))
    t1.start()
    t2.start()
    time.sleep(1)
    print(a)
    
if __name__ == "__main__":
    main()

\qquad 代码结果及解释:最后输出的结果是test1输出a的值为1000000,test2的a的值为1386825,最后输出a的值为1386825。按照前面所说的多线程共享全局变量有出入,原因是:电脑CPU再一个时刻只能运行一个线程,且在这一时刻运行那个线程由操作系统决定。上述代码运行时,test1线程在运行加1的代码时,可能来不及将新的值赋值给变量a,CPU就已经选择运行test2的线程了。
\qquad 解决办法:采用互斥锁,当test1的线程在运行的时候,使它处于锁定状态,直至test1的一个运算全部运算完才解锁,而当test1的线程运行的时候,其他线程只能等test1的 线程解锁之后才能运行。代码如下:

import threading
import time

a=0
mutex = threading.Lock()

def test1(num1):
    global a
    for i in range(num1):
        mutex.acquire()
        a=a+1
        mutex.release()
    print("--test1---%d---\n" % a)
        
def test2(num2):
    global a
    for i in range(num2):
        mutex.acquire()
        a=a+1
        mutex.release()
    print("--test2---%d---\n" % a)  
        
def main():
    t1 = threading.Thread(target=test1,args=(1000000,))
    t2 = threading.Thread(target=test2,args=(1000000,))
    t1.start()
    t2.start()
    time.sleep(2)
    print(a)

\qquad 代码结果及解释:代码最后的结果时test1输出a的值为1824762,而test2和最后输出的a的值都为2000000。test1输出的a的值不是1000000,test1的线程和test2的线程交互运行的结果。

\qquad 6)当有两个互斥锁的时候可能造成死锁。例子如下:

import threading
import time

a=0
mutex1 = threading.Lock()
mutex2 = threading.Lock()

class mythread1(threading.Thread):
    def run(self):
        mutex1.acquire()
        print(self.name+"---do1--up---")
        time.sleep(1)
        mutex2.acquire()
        print(self.name+"---do1--down--")
        mutex2.release()
        mutex1.release()

class mythread2(threading.Thread):
    def run(self):
        mutex2.acquire()
        print(self.name+"---do2--up---")
        time.sleep(1)
        mutex1.acquire()
        print(self.name+"---do2--down--")
        mutex1.release()
        mutex2.release()
        
def main():
    t1 = mythread1()
    t2 = mythread2()
    t1.start()
    t2.start()    
    
if __name__ == "__main__":
    main()

\qquad 代码结果及解释:最后只输出:“Thread-18—do1–up—”,
"Thread-19—do2–up—"没有打印出后面的"do—down—"是因为两个线程都在等着彼此运行的代码中解锁而阻塞了。
\qquad 解决办法:在代码中尽量避免死锁的形式;设置超时时间

\qquad 7) 多线程的udp聊天器

import threading
import socket

def send_data(udp_socket):
    while True:
        send_data1 = input("请输入你想发送的内容:")
        udp_socket.sendto(send_data1.encode("utf-8"),("192.34,34,2",6789))
        
def recv_data(udp_socket):
    while True:
        udp_socket.recvfrom(1024)
    
    
def main():
    udp_socket = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
    udp_socket.bind(("",7890))
    t1 = threading.Thread(target=send_data,args=(udp_socket,))
    t2 = threading.Thread(target=recv_data,args=(udp_socket,))
    t1.start()
    t2.start()
    
if __name__ == "__main__":
    main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值