Python ---- 多线程的详细解读

多线程

主线程 :主线程启动会有一个默认的主线,通常称为主线。

线程 可简单的理解为是程序执行的一条分支,也是程序执行流的最小单位。它是被系统独立调度和分派的基本单位。
主线程的作用:1)创建子线程 。
2)等其他子线程执行结束后,做关闭操作。

  • 子线程的创建

1.导入模块 threading

2.创建线程对象 threading.Tread(target=执行的分支函数名)

3.启动子线程。子线程对象.start()

'''
1、导入模块 threading 模块
2、创建线程类的对象
3、启动线程
'''
import threading
import time
def dance(name1):
    for i in range(5):
        print("%s在跳舞"%(name1))
        time.sleep(1)
def sing(name2):
    for i in range(5):
        print("%s在唱歌"%(name2))
        time.sleep(2)

Mytread1 = threading.Thread(target=dance,args=("小红",))
Mytread2 = threading.Thread(target=sing,args=("小明",))
Mytread1.start()
Mytread2.start()
  • 查看当前线程的数量

threading.enumerate 查看当前活跃线程对象
len( threading.enumerate) 查看线程数量

线程的名称 threading.current_thread().name

当前线程的对象 threading.current_thread() ,对象里有名称

import threading
import time
def dance(name1):
    for i in range(5):
        print("%s在跳舞"%(name1))
        time.sleep(1)
    print("当前的线程1",threading.current_thread().name)
def sing(name2):
    for i in range(5):
        print("%s在唱歌"%(name2))
        time.sleep(2)
    print("当前线程2",threading.current_thread().name)

Mytread1 = threading.Thread(target=dance,args=("小红",))
Mytread2 = threading.Thread(target=sing,args=("小明",))
print("当前所有线程3",threading.enumerate())
Mytread1.start()
print("当前所有线程4",threading.enumerate())
Mytread2.start()
print("当前所有线程5",threading.enumerate())
print("当前所有线程数",len(threading.enumerate()))
  • 线程的参数和顺序

在线程中有三种传递方法

1、使用元祖传递threading.Tread(target = 线程函数名称, args = (参数1,参数2…))

2、使用字典传递 Tread(target = 线程函数名称, kwargs = (‘a’:10,‘b’ = 20…)

3、使用元祖 字典传值 threading.Tread(target = 线程函数名称, args = (参数1,参数2…),kwargs = {‘a’:10,‘b’ = 20…)}**

线程执行顺序是无序的,由cpu 调度的,根据运行状态来调度 ,无法控制 。

import threading

def my_tread(a,b,c):
    print("参数:",a,b,c)

Mytread1 = threading.Thread(target=my_tread,args=(1,2,3))   #参数args 以元祖的形式
Mytread2 = threading.Thread(target= my_tread,kwargs={'a':10,'b':20,'c':30}) #参数 kwargs 以字典的形式
Mytread3 = threading.Thread(target= my_tread,args=(10,),kwargs={'b':20,'c':30}) #参数args, kwargs 以元祖、字典的混合形式
Mytread1.start()
Mytread2.start()
Mytread3.start()
  • 守护线程

    定义:

    ​ 如果在程序中将子线程设置为守护线程,则该线程会在主线程结束时自动退出。线程的运行 默认是主线程会等 着子线程执行结束会退出

​ 守护线程设置方式为子线程.setDaemon(True), 这种是主线程结束后,子线程会跟着结束

​ exit() 程序主动退出,主线程主动结束;(测试会用到)

​ 子线程守护主线程,就是说子线程和主线程的一种约定,主线程结束后子线程会跟着结束。

import threading
import time
def My_Tread():
    for i in range(10):
        print("我是子进程")
        time.sleep(1)

if __name__ == '__main__':
    print("主进程开始")
    Mytread = threading.Thread(target=My_Tread)
    Mytread.setDaemon(True)
    print("子进程开始运行")
    Mytread.start()
    time.sleep(5)
    print("主进程结束")
    exit()
  • 并发和并行

  • 并发: 任务数大于cpu核数;

  • 并行:任务数小于等于CPU核数 即 任务一起执行;

  • 自定义线程类

创建自定义线程

1、自定义类继承 threading.thread 类 如:class MyTread(treading.tread)

2、重写父类(threading.thread) run方法: def run(self)

3、通过创建子类对象,让子类对象.start() 就可以启动子线程,这里的 start() 方法 里面有 run()方法

​ Mythread = MyTread()

​ Mythread.start()

self.name 是从父类 继承 thread 类继承过来的

子类要调用 init 方法 先要重写父类方法的 super().init ()方法

底层原理: Treadl 类

  • run 方法

  • start 方法

  • start() 方法调用了run方法

  • 自定义中 __init__方法,子类要先通过super调用父类的初始化方法,子类再初始化

import threading
import time
class MyTread(threading.Thread):
    #类有参数的话,得初始化
    def __init__(self,name):
     # 因为MyTread类是继承父类,父类中也有init()方法,所以的重写父类的方法
        super().__init__()
        self.name = name
    #重写run()方法,PS:重写的意思是将写子类与父类的同名方法
    def run(self):
        for i in range(5):
            print("子线程程1:%s"%(self.name))
            time.sleep(1)
mytread = MyTread("小红")
mytread.start()
  • 多线程共享全局变量

多线程之间可以共享全局变量

import threading
import time
num = 0
def run1():
    global num
    for i in range(10):
        num +=1
    print("线程1",num)
def run2():
    print("线程2",num)
if __name__ == '__main__':
    T1 = threading.Thread(target=run1)
    T2 = threading.Thread(target=run2)
    T1.start()
    T2.start()
    while len(threading.enumerate()) != 1:
        time.sleep(1)
    print("主线程",num)

存在的问题:共享全局变量会导致资源竞争问题,当运算的数字足够大时会造成资源的竞争,影响数据的准确性。

解决方法:优先让某个线程先执行

​ 线程对象.join

缺点:会把多线程变成单线程,影响整体性能。

import threading
import time
num = 0
def run1():
    global num
    for i in range(100000):
        num +=1
    print("线程1",num)
def run2():
    global num
    for i in range(100000):
        num += 1
    print("线程2",num)
if __name__ == '__main__':
    T1 = threading.Thread(target=run1)
    T2 = threading.Thread(target=run2)
    T1.start()
    T1.join()  # 等待子线程T1运行结束后,主线程才向下运行
    T2.start()
    while len(threading.enumerate()) != 1:
        time.sleep(1)
    print("主线程",num)
  • 同步和异步

    • 同步:多任务,多个任务之间的执行的时候要求有先后顺序,必须一个执行完了之后,另一个才能执行,只有一个主线。
    • 异步:指的是,多个任务之间没有执行的顺序,可以同时执行,执行的先后顺序没有影响,存在多条主线。

    线程锁的机制:当一个线程获得一共享资源后,立即进行锁住,任务执行完成后再解锁,有效的保证同一时间只有一个线程在使用资源。

  • 同步互斥锁

    多个线程几乎同时修改一个共享数据的时候,需要进行同步控制。

    线程同步能够保证多个线程安全访问竞争资源,最简单的同步机制是引入互斥锁。

    互斥锁为资源引入一个状态:锁定/非锁定

    某个线程要改变共享资源时,先将其锁定,此时的资源的状态为“锁定”,其他线程不能更改;直到这个线程执行结束,释放资源,将资源的状态变成“非锁定”,其他线程才能再次锁定该资源。互斥锁保证了一次只能有一个线程进行写入操作,从而保证了多线程的情况下数据的正确性。

    • 创建互斥锁

      1、创建一把互斥锁: lock1 = threading.Lock()

      2、上锁 lock1.acqurie()

      3、解锁 lock1.release()

      互斥锁的使用原则:尽量少的锁定竞争资源

      import threading
      import time
      #创建一包锁
      lock1 = threading.Lock()
      num = 0
      def run1():
          global num
          for i in range(100000):
              #上锁
              lock1.acquire()
              num +=1
              #解锁
              lock1.release()
          print("线程1",num)
      def run2():
          global num
          for i in range(100000):
              #上锁
              lock1.acquire()
              num += 1
              #解锁
              lock1.release()
          print("线程2",num)
      if __name__ == '__main__':
          T1 = threading.Thread(target=run1)
          T2 = threading.Thread(target=run2)
          T1.start()
          T2.start()
          while len(threading.enumerate()) != 1:
              time.sleep(1)
          print("主线程",num)
      
  • 死锁

    在线程中共享多个资源的时候,如果两个线程分别占有一部分资源并且同时等待对方释放资源,就造成死锁。

    import threading
    def work1(index):
        a = [1, 3, 5, 7, 9]
        #上锁
        lock1.acquire()
        if index >= len(a):
            print("%d 下标越界"%(index))
            return
        print(a[index])
        #解锁
        lock1.release()
    if __name__ == '__main__':
        lock1 = threading.Lock()
        for i in range(10):
            mythread = threading.Thread(target=work1,args=(i,))
            mythread.start()
    '''
    运行结果
    1
    3
    5
    7
    9
    5 下标越界
    ''' 
    卡住了,下面几个线程都在等着释放资源,在return 前释放资源
    
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值