首页 > 编程语言 >并发编程之多线程操作篇

并发编程之多线程操作篇

时间:2024-01-21 22:00:13浏览次数:22  
标签:__ ... Thread threading 编程 并发 线程 time 多线程

多线程简单介绍

多线程,或者说多任务,指的是操作系统同时运行多个任务。例如,听歌、洗衣服、看视频可以同时进行,这种就是多任务。

单核CPU执行多任务:操作系统轮流让各个任务交替执行,任务1执行t1时间,切换到任务2,任务2执行t2时间,再切换到任务3,执行t3时间...如此反复执行,表面上看,每个任务交替执行,但是由于CPU的执行速度很快,在人看来就好像所有任务同时执行。

这里需要注意并发和并行的概念:

  • 并发,指任务数多于COU核数,通过操作系统的各种任务调度算法,实现用多个任务一起执行(实际上有一些任务没有在执行,但因为切换任务的速度相当快,看上去一起执行)。
  • 并行,指的是任务数小于或等于CPU核数,任务是同时执行的。

threading模块介绍

python中提供了thread和threading模块对线程进行操作,其中thread模块是比较底层的模块,threading模块对thread做了一些包装,使用更方便。

threading模块常用方法

方法 功能
threading.active_count() 返回当前处于active状态的Thread对象的个数
threading.current_thread() 返回当前Thread对象
threading.get_ident() 返回当前线程的线程标识
threading.enumerate() 返回当前处于active状态的所有Thread对象列表
threading.main_thread() 返回主线程对象,启动python解释器的线程对象
threading.stack_size() 返回创建线程时使用的栈的大小,如果指定 size参数,则用来指定后续创建的线程使用的 栈大小,size必须是0(标识使用系统默认值) 或大于32K的正整数

:线程标识是一个非负整数,并无特殊含义,只是用来标识线程,该整数可能会被循环利用,python3.3及以后版本支持该方法。

threading模块提供了Thread、Lock、RLock、Condition、Event、Timer和Semaphore等类支持多线程。

Thread类使用

Thread是threading提供的最重要也是最基本的类,可以通过该类创建线程并控制线程的运行。

使用Thread创建线程,有两种方式:

为构造函数传递一个可调用对象
继承Thread类并在子类中重写__init__()和run()
threading.Thread类常用的方法和属性如下

方法 功能
start() 启动线程
run() 线程代码,用来实现线程的功能和业务逻辑,可以在子类中重写该方法自定义线程的行为
init(self,group=None,target=None,name=None,args=(),kwargs=None,daemon=None) 构造函数
is_alive() 判断线程是否存货
getName() 返回线程名
setName() 设置线程名
isDaemon() 判断线程是否为守护线程
setDaemon() 设置线程是否为守护线程
  • 常用属性
属性名 功能
name 读取或设置线程的名字
ident 线程标识,用非0数字或None(线程未被启动)
daemon 标识线程是否为守护线程,默认为False
join(timeout=None) 当timeout=None时,会等待至线程结束;

threading.Thread类的参数介绍:

threading.Thread(group=None,
    target=None,
    name=None,
    args=(),
    kwargs={},
    *,
    daemon=None)
参数 group 通常默认,作为扩展ThreadGroup类保留。
参数 target 用于run()方法调用的可调用对象,默认为None,标识run()不调用任何对象。
参数 name 线程名称,默认是Thread-N格式狗策划给你的唯一名称,N是十进制数。
参数 args  用于调用目标函数的参数元组,默认是()。
参数 kwargs  用于调用目标函数的关键字参数字典,默认为{}。
参数 daemon 设置线程是否为守护模式,默认为None。

开启线程的两种方式

  • 开启线程不需要在main下面执行代码,直接书写即可
  • 但是我们还是习惯性的将启动命令写在main下面

方式一:使用构造函数传递可调用对象的方法创建线程

示例1 :使用threading模块创建单线程

import threading
import time


def sing():
    print("正在唱歌...")
    time.sleep(1)


if __name__ == "__main__":
    print("======================调用函数======================")
    start_time = time.time()
    print("开始时间:%s" % time.ctime())
    for i in range(10):
        sing()
    print("结束时间:%s" % time.ctime())
    print(f"总共耗时{time.time() - start_time}s")
    print("==========threading.Thread.start()开启单线程==========")

    print("开始时间:%s" % time.ctime())
    start_time = time.time()
    for i in range(10):
        t = threading.Thread(target=sing)
        t.start()
    print("结束时间:%s" % time.ctime())
    print(f"总共耗时{time.time() - start_time}s")

显示结果,可以看出来使用threading.Thread启动线程时,使用的时间要短很多。

======================调用函数======================
开始时间:Fri Jan 19 16:36:43 2024
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
结束时间:Fri Jan 19 16:36:53 2024
总共耗时10.00361943244934s
==========threading.Thread.start()开启单线程==========
开始时间:Fri Jan 19 16:36:53 2024
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
正在唱歌...
结束时间:Fri Jan 19 16:36:53 2024
总共耗时0.0010197162628173828s

当调用start()时,才开始真正的创建线程,并开始执行。

示例2:使用threading.Thread创建多线程,并查看当前处于active状态的线程的数量。

import threading
from time import ctime, sleep


def sing():
    for i in range(3):
        print("singing...%d" % i)
        sleep(1)


def dance():
    for i in range(3):
        print("dancing...%d" % i)
        sleep(1)


if __name__ == "__main__":
    print("---start---:%s" % ctime())

    t1 = threading.Thread(target=sing)
    t2 = threading.Thread(target=dance)

    t1.start()
    t2.start()

    while True:
        length = len(threading.enumerate())
        print("current thread has %d" % length)
        if length <= 1:
            break

        sleep(0.5)

    print("---end---:%s" % ctime())

结果:

---start---:Fri Jan 19 16:43:22 2024
singing...0
dancing...0
current thread has 3
current thread has 3
dancing...1
singing...1
current thread has 3
current thread has 3
singing...2dancing...2

current thread has 3
current thread has 3
current thread has 1
---end---:Fri Jan 19 16:43:25 2024

方式二:继承threading.Thread类

在使用threading模块时,也可以定义一个新的类class,继承threading.Thread,然后重写run()。

继承threading.Thread来定义线程类,其本质是重构Thread类中的run方法

threading.Thread类中的run方法,用于定义线程的功能函数,可以在自己的线程类中覆盖该方法。创建自己的线程实例后,通过Thread类的start方法,可以启动该线程。

示例1:创建一个继承自threading.Thread类的子类

import threading
import time


class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm " + self.name + " | " + str(i)  # name保存当前线程的名字
            print(msg)


if __name__ == "__main__":
    t = MyThread()
    t.start()
    
# I'm Thread-1 | 0
# I'm Thread-1 | 1
# I'm Thread-1 | 2

示例2:观察线程执行的顺序

import threading
import time


class MyThread(threading.Thread):
    def run(self):
        for i in range(3):
            time.sleep(1)
            msg = "I'm " + self.name + " | " + str(i)  # name保存当前线程的名字
            print(msg)


def test():
    for i in range(5):
        t = MyThread()
        t.start()


if __name__ == "__main__":
    test()
    
# I'm Thread-2 | 0
# I'm Thread-1 | 0
# I'm Thread-4 | 0
# I'm Thread-5 | 0
# I'm Thread-3 | 0
# I'm Thread-1 | 1
# I'm Thread-2 | 1
# I'm Thread-3 | 1
# I'm Thread-5 | 1
# I'm Thread-4 | 1
# I'm Thread-1 | 2
# I'm Thread-2 | 2
# I'm Thread-5 | 2
# I'm Thread-3 | 2
# I'm Thread-4 | 2

每次运行时显示的执行顺序是不确定的。当执行到sleep()的时候,当前线程被阻塞,sleep()结束后当前线程进入就绪状态,等待调度。

线程调度会自行选择一个线程执行。

如上代码只能保证每个线程都能运行完整个run函数,但线程的启动顺序、run函数中每次循环的执行顺序都不能确定。

总结

  1. 每个线程默认有一个名字,没有指定,python会自动为线程指定一个名字
  2. 当线程的run()方法结束时该线程完成;
  3. 无法控制线程调度程序,可以通过别的方式影响线程调度方式。

线程对象的 join 方法

from threading import Thread
import time


def task(name):
    print(f'the task {name} is beginning')
    time.sleep(3)
    print(f'the task {name} is ending')


def main():
    t = Thread(target=task, args=('xiao',))
    t.start()

    # 主线程等待子进程结束之后再运行
    t.join()

    print(f'the task is main task')


if __name__ == '__main__':
    main()
    
    # the task xiao is beginning
    # the task xiao is ending
    # the task is main task

同一个进程下的多个线程之间数据是共享的

from threading import Thread
import time

money = 999


def task():
    global money
    money = 99
    print(f'task中的money:>>>>{money}')


def main():
    print(f'子进程之前的money:>>>>{money}')
    t = Thread(target=task)
    t.start()
    print(f'子进程之后的money:>>>>{money}')


if __name__ == '__main__':
    main()

    # 子进程之前的money:>>>>999
    # task中的money:>>>>99
    # 子进程之后的money:>>>>99

标签:__,...,Thread,threading,编程,并发,线程,time,多线程
From: https://www.cnblogs.com/xiao01/p/17978441

相关文章

  • 并发编程之操作系统引入
    一、引言顾名思义,进程就是正在执行的一个过程。进程是对正在运行程序的一个抽象说法。所谓进程,起源于操作系统最核心的概念,操作系统的其他所有内容都是围绕进程的概念展开的。所以想要真正了解进程,必须事先了解操作系统。二、为什么要有操作系统现代的计算机系统主要是由一个......
  • 并发编程之多进程理论篇
    引言进程是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是......
  • JUC并发编程 线程中断介绍及相关Api方法
    什么是中断机制首先,一个线程不应该由其他线程来强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运,所以,Thread.stop,Thread.suspend,Thread.resume都已经被废弃了其次,在Java中没有办法立即停止一条线程,然而停止线程却显得尤为重要,如取消一个耗时操作。因此,Java提供......
  • Win32 编程 Windows API 使用(二)
     有了上一章的实验,我们来创建第一个自己设计的窗口按照之前的步骤,再创建一个新的项目,文件名称是app.cpp(也可以是app.c),并将这个类取名为MyApp,即最后运行的程序为MyAPP.exe 运行一遍发现这个窗口没法拖动,也没办法关闭。但是,窗口也确实显示出来了。#include<window......
  • Win32 编程 Windows API 使用(一)
    1.WindowAPI通常,Win32是指WindowsAPI,也指MicrosoftWindows操作系统的32位环境,和Win64 Windows常见环境。早期的操作系统为16位(window1.0),Win98/XP之后出现32位和64位,Win32即也是32位和64位的统称。所谓Win32编程,即主要使用GUI构图,编写一套程序。除了使用原生的Wind......
  • C++多线程3
    1利用栈特性自动释放锁RAII1.1什么是RAIIRAII(ResourceAcquisitionIsInitialization),使用局部对象管理资源的技术称为资源获取既初始化,它的生命周期由操作系统管理,无需人工干预。为什么要引入自动释放锁,因为我们有时会因为忘记释放锁,而造成死锁或内存泄漏。我们先来手动实......
  • 多线程与多进程
    多线程和多进程爬虫在很多场景中,爬虫需要抓取大量的数据,而且需要做大量的分析工作。如果只使用单线程的爬虫,效率会非常低。通常有实用价值的爬虫会使用多线程或多进程,这样可以很多工作同时完成,尤其在多CPU的机器上,执行效率更是惊人。一、线程与进程1、进程计算机程序有静态和......
  • AOP 编程
    AOP编程目录AOP编程1.AOP和OOP2.AOP中的一些概念术语通知类型3.AOP实现方式4.AOP使用1.添加依赖:在项目的pom.xml文件中添加SpringAOP依赖,以确保AOP模块可用。2.创建切面类:创建一个Java类,并使用@Aspect注解标记它,这个类将充当切面。1.AOP和OOPAOP为AspectOrie......
  • 《Java并发实现原理:JDK源码剖析》PDF
    《Java并发实现原理:JDK源码剖析》全面而系统地剖析了JavaConcurrent包中的每一个部分,对并发的实现原理进行了深刻的探讨。全书分为8章,第1章从最基础的多线程知识讲起,理清多线程中容易误解的知识点,探究背后的原理,包括内存重排序、happen-before、内存屏障等;第2~8章,从简单到复杂,逐......
  • Java多线程
    Java多线程名词解释程序(program)是为完成特定任务、用某种语言编写的一组指令集合。简单而言:就是自己写的代码进程(Process)进程是指运行中的程序,比如启动迅雷时,就启动了一个进程,操作系统就会为该进程分配内存空间。进程是程序的一次执行过程,或是正在运行的一个程序。是......