python的多线程

多线程是多任务的一种方式,多线程的目的就是解决并发,提高处理效率.

多线程有两个概念:


    1.并行:并行的前提是必须是多核CPU才可以发生.

                并行的意思就是同时执行,成为并行

    2.并发:并发可以发生在单核CPU和多核CPU中.

                并发的意思就是在一定时间内执行任务,成为并发

一.创建多线程的方法有两种

    (一).threading,Thread类的实例对象,即创建一个threading.Thread对象

import time
import threading


def say_sorry():
    print("世纪东方垃圾收快递费了")
    time.sleep(1)


if __name__ == '__main__':
    for i in range(5):
        # Thread是一个类,创建一个线程执行的计划,target指定线程在执行的时候执行的函数
        # threading.Thread返回的是一个Thread对象
        thd = threading.Thread(target=say_sorry)
        # 调用start()方法,创建一个线程,并且执行这个线程
        thd.start()

    # threading.enumerate()返回当前所有的线程
    print(threading.enumerate())
    # 打印当前执行的线程的数量
    print(len(threading.enumerate()))

(二).第二种创建线程的方式为继承threading,Thread类,成为他的子类,并重写run()方[常用]

import threading
import time


class my_thread(threading.Thread):
    """继承threading.Thread类"""
    
    # 重写run()方法
    def run(self):
        for i in range(500):
            print("子线程执行了")
            time.sleep(0.2)


if __name__ == '__main__':
    # 创建自己的线程对象
    mt = my_thread()

    # 调用当前对象的Tread子类的start()方法的时候,默认执行的run()方法
    mt.start()

    while True:
        # 打印执行的线程
        print(threading.enumerate())
        time.sleep(2)
    线程的调度是根据cpu的调度方式决定的,window是抢占式调度,linux是一次调度(不确定,暂时这么记)
在线程调度时,默认情况下主线程会等待子线程全部执行完再释放

    查看当前所执行的线程:threading.enumerate().返回一个元祖

二.给子线程传递参数

    (一).创建threading.Thread对象创建线程的方式,给线程传参的代码如下

import threading
from time import sleep


def sing(num1, num2):
    for i in range(3):
        print("唱歌%d 参数1=%d 参数2=%d" % (i, num1, num2))
        sleep(1)


def dance(str1, str2):
    for i in range(3):
        print("跳舞%d参数1=%s参数2=%s" % (i, str1, str2))
        sleep(1)


if __name__ == '__main__':
    # 创建唱歌的执行计划
    # 给线程传递参数用args,args是一个元祖,
    sing_thd = threading.Thread(target=sing, args=(100, 22))
    dance_thd = threading.Thread(target=dance, args=("dd", "的方式"))

    # 创建线程并且执行
    sing_thd.start()
    dance_thd.start()

    (二).实现threading.Thread类,创建线程的方法,给线程传递参数的代码如下(自己试验)

    注意:在创建自己的线程对象的时候,给自己线程对象,初始化参数之前,必须调用threadingt.Thread.__init__()方法,为线程初始化,否则会报错,显示自己的线程对象的__init__()没有被调用

import threading
import time


class my_thread(threading.Thread):

    # 给线程传递参数,创建一个变量
    def __init__(self, num):
        # 在自己的线程类中重写init方法时,必须先调用threasing.Thread的init方法,
        # 如果没有先初始化线程对象,将会报错Python RuntimeError: thread.__init__() not called异常
        threading.Thread.__init__(self)
        self.num = num

    def run(self):
        for e in range(3):
            time.sleep(1)
            print(self.name+"----------"+str(e)+"传入的参数%d" % self.num)


if __name__ == '__main__':

    # for i in range(5):
    mt = my_thread(11)
    mt.start()

    # while True:
    #     if len(threading.enumerate()) == 1:
    #         print("就剩我自己 了吧")
    #         break
    mt.join(10)
    print("子线程全部执行完了")

三.如有需求要在所有子线程执行后,在执行部分逻辑,有以下两种方法可以实现

    一个是判断当前执行的线程的元祖的数量的长度是否等于1,如果等于1,说明只剩下了主线程,代码如下:

    

  # for i in range(5):
    mt = my_thread(11)
    mt.start()

    # while True:
    #     if len(threading.enumerate()) == 1:
    #         print("就剩我自己 了吧")
    #         break

    二是调用threading.Thread或者其子类的join()方法:

if __name__ == '__main__':

    # for i in range(5):
    mt = my_thread(11)
    mt.start()

    # join()方法是判断是否所有子线程已经执行完,若没有执行完,将堵塞本线程,不向下执行
    # 如所有子线程已经执行完,将执行join()后面的代码
    # join(10)参数10,为判断是否还有子线程的超时时间
    mt.join(10)
    print("子线程全部执行完了")

四.同一个进程中多个线程共享数据的问题

    若同一个进程中多个线程共享一个数据,将会出现数据的安全问题,即非线程安全

    解决非线程安全的一个方法就是,在对共享数据进行更改时,先去判断这资源是否被其他线程使用,

    互斥锁:即对资源的锁定,有两种状态,分别为锁定/非锁定

        1.创建锁对象:lock = threading.Lock()

        2. 将锁对象传入每个线程中

        3.在对共享数据修改之前调用所对象的lock.acquire()---[acquire:"获得"]方法

            

# acquire()的含义就是去尝试的获取锁,如果没有被锁定,将会被我锁定.
# 如果已经被锁定,那么将阻塞等待,直到另一方释放锁,然后自己得到锁.继续执行

        4.处理完共享数据之后,需要释放锁,以便于其他线程获取数据,调用lock.release()-----[release:释放]

           

    这种处理锁的方法,在多线程执行时,效率低,容易造成死锁.

    对共享数据的处理如下:

import threading
import time
g_number = 0

# TODO 两个线程同时对一个全局变量进行修改,会发生非线程安全的问题
# TODO 解决方案:同步线程,为共享资源添加互斥锁,互斥锁有两种状态:锁定/非锁定
# TODO 共享资源只允许一个资源访问


def worker1(lock1):
    global g_number

    for i in range(1000000):
        # 在修改全局变量之前,调用lock()方法,
        # acquire()的含义就是去尝试的获取锁,如果没有被锁定,将会被我锁定.
        # 如果已经被锁定,那么将阻塞等待,直到另一方释放锁,然后自己得到锁.继续执行
        lock1.acquire()

        g_number += 1

        # 释放锁
        lock1.release()


def worker2(lock1):
    global g_number

    for i in range(1000000):
        lock1.acquire()
        g_number += 1
        lock1.release()


if __name__ == '__main__':
    # 创建一把锁
    lock = threading.Lock()

    worker1_thread = threading.Thread(target=worker1, args=(lock,))
    worker2_thread = threading.Thread(target=worker2, args=(lock,))
    worker1_thread.start()
    worker2_thread.start()

    # 等两个线程执行完 再执行下面的代码
    worker1_thread.join()
    worker2_thread.join()

    print(g_number)


未完待续...

















  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值