欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

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

程序员文章站 2024-03-24 22:05:22
...

多线程

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

线程 可简单的理解为是程序执行的一条分支,也是程序执行流的最小单位。它是被系统独立调度和分派的基本单位。
主线程的作用: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 前释放资源