多任务的实现者——线程

4 篇文章 0 订阅
3 篇文章 0 订阅

线程是什么

线程简单的理解是程序执行的一条分支,也可以是程序执行的最小单元。是被系统独立调度和分派的基本单位。

线程的创建

通过threading模块创建子线程
代码如下:

import threading
import time
#定义函数
def a():
    print("唱歌")
    time.sleep(0.5)

if __name__ == '__main__':
    for i in range(5):
        #创建线程
        A=threading.Thread(target=a)
        #启动线程
        A.start()
        

结果:
在这里插入图片描述
线程与不加线程的对比:
在这里插入图片描述
由上我们知道,多线程执行节约好多时间。主线程等待子线程结束了才结束。

实例方法

通过getName()获取线程的名称
通过setName()获取线程的名称
通过isAlive()判断当前线程状态
代码说明:

import threading
import time
def sing(num):
    for i in range(num):
        print("正在唱歌",i)
        time.sleep(1)

def dance(num):
    for i in range(3):
        print("正在跳舞",i)
        time.sleep(1)

if __name__ == '__main__':
    t1=threading.Thread(target=sing,args=(3,))
    t2=threading.Thread(target=dance,args=(3,))
    #设置线程名称
    t1.setName("张飞")
    t2.setName("关羽")
    #获取线程名称
    t1_name=t1.getName()
    t2_name=t2.getName()
    print(t1_name)
    print(t2_name)
    #判断线程是否存活
    print(t1.is_alive())
    t1.start()
    t2.start()
    print(t2.is_alive())

结果:
在这里插入图片描述

threading模块方法

通过current_thread()查线程的名称
通过threading.enumerate()获取正在运行活的线程
通过 threading.active_count()获取活的线程数 等同于 len(threading.enumerate())
代码说明:

import threading
import time
def sing(num):
    for i in range(num):
        print("正在唱歌",i)
        time.sleep(1)

def dance(num):
    for i in range(num):
        print("正在跳舞",i)
        time.sleep(1)
    print("t2-->",threading.currentThread())

if __name__ == '__main__':
    t1=threading.Thread(target=sing,args=(3,))
    t2=threading.Thread(target=dance,args=(3,))
    t1.start()
    t2.start()
    #获取当前线程的变量名称
    t_nome=threading.current_thread()
    #获取正在运行的活线程
    print(threading.enumerate())

    #获取活线程的个数
    print(threading.active_count())
    print(len(threading.enumerate()))

结果:
在这里插入图片描述

继承方式开启线程

方法:1.首先我们定义 一个类继承threading.Thread类
2.复写父类run()方法
代码:

from  threading import Thread
# 继承方法创建
class MyThread(Thread):
    def __init__(self,num):
        super().__init__()
        self.num=num
    def run(self):
        for i in range(self.num):
            print("线程{}".format(i+1))
if __name__ == '__main__':
    my_thread=MyThread(3)
    my_thread.start()

结果:
在这里插入图片描述
从上我们可以看到通过继承的方式也可以开启线程。

线程参数顺序及传递问题

线程参数传递有三种方式:
第一种方式:通过元组进行传值

import threading
import time
def sing(a,b):
    for i in range(a):
        print("正在唱歌",i,b)
        time.sleep(1)

def dance(c,d):
    for i in range(c):
        print("正在跳舞",i,d)
        time.sleep(1)

if __name__ == '__main__':
    t1=threading.Thread(target=sing,args=(3,"风筝舞"))
    t2=threading.Thread(target=dance,args=(3,"风筝舞"))
    t1.start()
    t2.start()

第二种方式:通过字典传值

import threading
import time
def sing(a,b):
    for i in range(a):
        print("正在唱歌",i,b)
        time.sleep(1)

def dance(c,d):
    for i in range(c):
        print("正在跳舞",i,d)
        time.sleep(1)

if __name__ == '__main__':
    t1=threading.Thread(target=sing,kwargs={"a":3,"b":"风筝舞"})
    t2=threading.Thread(target=dance,kwargs={"c":3,"d":"风筝舞"})
    t1.start()
    t2.start()

第三种方式:元组和字典共同传值

import threading
import time
def sing(a,b):
    for i in range(a):
        print("正在唱歌",i,b)
        time.sleep(1)

def dance(c,d):
    for i in range(c):
        print("正在跳舞",i,d)
        time.sleep(1)

if __name__ == '__main__':
    t1=threading.Thread(target=sing,args=(3, ),kwargs={"b":"风筝舞"})
    t2=threading.Thread(target=dance,args=(3, ),kwargs={"d":"风筝舞"})
    t1.start()
    t2.start()

结果:
在这里插入图片描述
不论通过上面那种方法传值都可接收到,但在第三种上要按顺序书写。

标题线程之间共享全局变量

首先我们定义一个全局变量用代码来解释:

import random
g_num=100
def work1():
    global g_num
    for i in range(3):
        g_num+=1
        time.sleep(random.random())
        print("in work1,g_num=%d"%g_num)
def work2():
    global g_num
    for i in range(3):
        g_num += 1
        time.sleep(random.random())
        print("in work2,g_num=%d" % g_num)
if __name__ == '__main__':
    t1 = threading.Thread(target=work1)
    t2=threading.Thread(target=work2)
    t1.start()
    t2.start()

结果:
在这里插入图片描述
我们在线程之间共享全局变量,通过上面我们发现在模拟共享的过程中出现了资源竞争,会影响结果,我们可以 简单的处理这种问题,通过join()可以解决。
代码:

import threading
import time
import random
g_num=100
def work1():
    global g_num
    for i in range(3):
        g_num+=1
        time.sleep(random.random())
        print("in work1,g_num=%d"%g_num)
def work2():
    global g_num
    for i in range(3):
        g_num += 1
        time.sleep(random.random())
        print("in work2,g_num=%d" % g_num)
if __name__ == '__main__':
    t1 = threading.Thread(target=work1)
    t2=threading.Thread(target=work2)
    t1.start()
    t1.join()
    t2.start()

结果:
在这里插入图片描述
这么一来一个线程执行完在让另一个线程,不会出错。下来我们就专门来解决一下这个资源竞争问题。

线程资源竞争问题

为了解决多线程几乎同时修改一个共同数据的时候,需要进行同步控制,线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。
创建互斥锁有三步:
1.创建互斥锁
2.在使用资源前锁定资源
3.使用完毕资源之后解锁资源
创建如下:

import threading
import time
g_num=0
def work1():
    global g_num
    for i in range(1000000):
        # 上锁
        lock1.acquire()
        g_num += 1
        # 解锁
        lock1.release()
    print("work1--->",g_num)
def work2():
    global g_num
    for i in range(1000000):
        #上锁
        lock1.acquire()
        g_num += 1
        #解锁
        lock1.release()
    print("work2--->",g_num)

if __name__ == '__main__':
    #创建互斥锁
    lock1 = threading.Lock()

    #创建2个线程
    t1 = threading.Thread(target=work1)
    t2=threading.Thread(target=work2)
    #启动两个线程
    t1.start()
    t2.start()
    while len(threading.enumerate()) != 1:
        time.sleep(1)
    #在t1和t2执行完后在主线程打印g_num
    print("main---->",g_num)

结果:
在这里插入图片描述
通过互斥锁也可以按照join()的模式运行,把一个线程运行完在运行另一个线程,
代码如下:

import threading
import time
g_num=0
def work1():
    global g_num
    # 上锁
    lock1.acquire()
    for i in range(1000000):
        g_num += 1
    # 解锁
    lock1.release()
    print("work1--->",g_num)
def work2():
    global g_num
    # 上锁
    lock1.acquire()
    for i in range(1000000):
        g_num += 1
    # 解锁
    lock1.release()
    print("work2--->",g_num)

if __name__ == '__main__':
    #创建互斥锁
    lock1 = threading.Lock()

    #创建2个线程
    t1 = threading.Thread(target=work1)
    t2=threading.Thread(target=work2)
    #启动两个线程
    t1.start()
    t2.start()
    while len(threading.enumerate()) != 1:
        time.sleep(1)
    #在t1和t2执行完后在主线程打印g_num
    print("main---->",g_num)

结果 :
在这里插入图片描述
这个方法固然好,但是不能把互斥锁写成死锁,在线程之间共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方的资源,这样就形成了死锁。

def get_value(index):
    data_list=[1,3,5,7,9]
    #上锁
    lock1.acquire()
    # 判断下标是否越界
    if index >= len(data_list):
        print("下标越界!",index)
        # # 解锁
        # lock1.release()
        return
    print(data_list[index])
    #解锁
    lock1.release()
#创建10个线程,观察资源等待
if __name__ == '__main__':
    #创建锁
    lock1=threading.Lock()
    # 创建10个线程
    for i in range(10):
        t1=threading.Thread(target=get_value,args=(i, ))
        t1.start()

结果:
在这里插入图片描述
在越界后资源的不到释放会依然等待,如果加上return返回前加上解锁就不会处于等待状态了,就不会形成死锁。

# 解锁
        # lock1.release()

在这里插入图片描述

线程队列Queue

q=Queue.Queue(10) 创建队列
q.put() 放入值
q.get() 取出值
q.get_nowait() 取不到数据直接抛出异常不等待
q.empty() 判断队列是否为空
q.full() 判断队列是否已满
q.task_done() 一项执行完成后发信号给 q.join()不会形成阻塞

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值