首页 > 其他分享 >多线程

多线程

时间:2024-01-28 17:33:47浏览次数:23  
标签:__ 多线程 run task 线程 main ID

多线程理论

(1)什么是线程

  • 在 Python 中,线程(Thread)是执行单元的最小单位。线程是进程内的一条执行路径,每个线程都有自己的执行序列、执行环境和栈空间,但它们共享同一个进程的地址空间。

  • 在多线程编程中,可以同时运行多个线程,每个线程执行不同的任务,从而实现并发执行。相比于多进程,线程的开销较小,线程之间的切换更快,因为它们共享进程的内存空间。

  • 在 Python 中,线程可以通过标准库中的 threading 模块来创建和管理。通过创建 Thread 对象并将要执行的函数传递给该对象,就可以创建一个新的线程。Python 提供了丰富的线程控制和同步机制,如互斥锁、信号量等,用于协调和管理多个线程之间的并发访问。

  • 需要注意的是,在 Python 中由于全局解释器锁(GIL)的存在,多线程并不能真正实现多核并行计算,因为在任何给定时刻只有一个线程能够执行 Python 字节码。因此,在 CPU 密集型任务中,多线程并不能显著提高性能,但对于 I/O 密集型任务,多线程可以提高程序的响应速度和并发处理能力。

(2)线程的创建开销

  • 线程的创建开销包括多个方面,主要取决于操作系统和编程语言的实现。在 Python 中,线程的创建开销相对较高,主要原因是因为 Python 的全局解释器锁(Global Interpreter Lock,GIL)以及线程对象的额外开销。

  • 以下是影响线程创建开销的一些因素:

  1. 内核态和用户态切换: 线程是由操作系统内核来创建和管理的,因此线程的创建需要进行内核态和用户态之间的切换,这个切换过程会产生一定的开销。
  2. 内存分配和资源管理: 每个线程都需要分配一定的内存空间来存储线程的执行环境、栈空间以及其他相关信息。此外,操作系统需要为每个线程分配一些资源,如线程 ID、线程控制块等。
  3. GIL 的影响: 在 Python 中,由于全局解释器锁(GIL)的存在,多个线程不能同时执行 Python 字节码,因此线程的并行性受到限制。线程创建时,由于 GIL 的存在,可能会导致一定的额外开销。
  4. 线程对象的额外开销: 在 Python 中,线程对象(Thread)的创建也会带来一定的开销,包括线程对象本身的内存占用、管理开销等。
  5. 竞争和同步开销: 如果线程需要访问共享资源或进行同步操作,还会引入额外的竞争和同步开销,如锁的获取和释放等。
  • 总的来说,虽然线程的创建开销相对较低,但在 Python 中,由于 GIL 的存在以及额外的线程对象开销等因素,线程的创建开销可能会比较显著。因此,在设计多线程应用程序时,需要谨慎考虑线程的创建数量和资源管理,以避免不必要的开销和性能下降。

(3)线程和进程的区别

线程(Thread)和进程(Process)是操作系统中的两个核心概念,用于实现并发执行任务。它们之间的主要区别在于以下几个方面:

  1. 执行单位:

    • 进程是操作系统中的一个独立执行单元,拥有独立的地址空间、内存、文件描述符等资源。每个进程都有自己的代码段、数据段、堆栈等。
    • 线程是进程内的一个独立执行单元,多个线程共享同一个进程的地址空间和资源,包括内存、文件描述符、打开的文件等。因此,多个线程之间可以共享数据和通信。
  2. 资源消耗:

    • 进程之间的资源是相互独立的,因此进程的创建和销毁比较耗费系统资源,包括内存和 CPU 时间。
    • 线程之间共享相同的资源,因此线程的创建和销毁开销相对较小,但是线程之间的同步和通信会引入额外的开销。
  3. 通信与同步:

    • 进程之间的通信和同步相对复杂,通常需要使用进程间通信(Inter-Process Communication,IPC)机制,如管道、消息队列、共享内存等。
    • 线程之间共享同一个进程的地址空间和资源,因此线程之间的通信和同步相对简单,可以直接通过共享变量等方式进行通信和同步。
  4. 并发性和性能:

    • 由于进程之间拥有独立的资源,因此多进程可以实现真正的并行执行,适合处理 CPU 密集型任务。
    • 线程共享相同的资源,因此多线程更适合处理 I/O 密集型任务,但由于全局解释器锁(GIL)的存在,Python 中的多线程并不能真正实现多核并行,适合处理 I/O 密集型任务和简单的并发处理。

总的来说,进程是系统中的独立执行单元,拥有独立的资源,适合处理 CPU 密集型任务;而线程是进程内的独立执行单元,共享进程的资源,适合处理 I/O 密集型任务和简单的并发处理。

(4)为何要有多线程

(1)开设进程

  • 申请内存空间 -- 耗资源
  • 拷贝代码 - 耗资源

(2)开设线程

  • 一个进程内可以开设多个线程
  • 在一个进程内开设多个线程无需再次申请内存空间及拷贝代码操作

(3)总结线程的优点

  • 减少了资源的消耗
  • 同一个进程下的多个线程资源共享

(4)什么是多线程

  • 多线程指的是
    • 在一个进程中开启多个线程
    • 简单的讲:如果多个任务共用一块地址空间,那么必须在一个进程内开启多个线程。
  • 多线程共享一个进程的地址空间
    • 线程比进程更轻量级,线程比进程更容易创建可撤销,在许多操作系统中,创建一个线程比创建一个进程要快10-100倍,在有大量线程需要动态和快速修改时,这一特性很有用
  • 若多个线程都是cpu密集型的,那么并不能获得性能上的增强
    • 但是如果存在大量的计算和大量的I/O处理,拥有多个线程允许这些活动彼此重叠运行,从而会加快程序执行的速度。
  • 在多cpu系统中,为了最大限度的利用多核,可以开启多个线程,比开进程开销要小的多。(这一条并不适用于Python)

多线程开设

(1)threading模块

import time
from threading import Thread


def run(i):
    print(f'这是线程{i}')
    time.sleep(2)


def main_first():
    task_list = []
    for i in range(1, 5):
        thread = Thread(target=run, args=(i,))
        task_list.append(thread)
    task_list_new = []
    for i in task_list:
        i.start()
        task_list_new.append(i)
    for p in task_list_new:
        p.join()


if __name__ == '__main__':
    start_time = time.time()
    main_first()
    print(f'总耗时:>>> {time.time() - start_time}')

# 这是线程1
# 这是线程2
# 这是线程3
# 这是线程4
# 总耗时:>>> 2.012131929397583

(2)继承Thread父类

from threading import Thread
import time


class MyThread(Thread):

    def __init__(self, name):
        super().__init__()
        self.name = name

    # 定义 run 函数
    def run(self):
        print(f'{self.name} is running')
        time.sleep(3)
        print(f'{self.name} is ending')


def main():
    t = MyThread('heart')
    t.start()
    print(f'this is a main process')


if __name__ == '__main__':
    main()

# heart is running
# this is a main process
# heart is ending

(3)查看pid

  • 多个线程其实是开设在一个进程下的,子线程的进程id和主线程是一样的
import time
from multiprocessing import Process
from threading import Thread
import os


def run(i):
    print(f'这是参数:>>> {i}')
    print(f'这是子的ID:>>> {os.getpid()}')
    time.sleep(2)

def main_process():
    task_list = [Process(target=run, args=(i,)) for i in range(1, 5)]
    [task.start() for task in task_list]
    [task.join() for task in task_list]
    print(f'这是主进程的ID :>>> {os.getpid()}')

def main_thread():
    task_list = [Thread(target=run, args=(i,)) for i in range(1, 5)]
    [task.start() for task in task_list]
    [task.join() for task in task_list]
    print(f'这是主线程的ID :>>> {os.getpid()}')

if __name__ == '__main__':
    main_process() 
	# 这是参数:>>> 2
    # 这是子的ID:>>> 5644
    # 这是参数:>>> 1
    # 这是子的ID:>>> 1904
    # 这是参数:>>> 3
    # 这是子的ID:>>> 13532
    # 这是参数:>>> 4
    # 这是子的ID:>>> 11968
    # 这是主进程的ID :>>> 10196
    
    main_thread()
    # 多个线程其实是开设在一个进程下的,子线程的进程id和主线程是一样的
    # 这是参数:>>> 1
    # 这是子的ID:>>> 16416
    # 这是参数:>>> 2
    # 这是子的ID:>>> 16416
    # 这是参数:>>> 3
    # 这是子的ID:>>> 16416
    # 这是参数:>>> 4
    # 这是子的ID:>>> 16416
    # 这是主线程的ID :>>> 16416

(4)多线程共享数据

  • 同一个进程下的所有线程共享同一个进程的资源
  • 多进程是不共享的,每一块内存空间都是独立的
  • 多进程的代码和结果如下
import time
from multiprocessing import Process
import os

number = 0


def run(i):
    global number
    number += 1
    print(f'这是run函数内的number:>>> {number}')
    print(f'这是参数:>>> {i}')
    print(f'这是子的ID:>>> {os.getpid()}')
    time.sleep(2)


def main_process():
    task_list = [Process(target=run, args=(i,)) for i in range(1, 5)]
    [task.start() for task in task_list]
    [task.join() for task in task_list]
    print(f'这是主进程的ID :>>> {os.getpid()}')

if __name__ == '__main__':
    main_process()

# 这是run函数内的number:>>> 1
# 这是参数:>>> 3
# 这是子的ID:>>> 15116
# 这是run函数内的number:>>> 1
# 这是参数:>>> 2
# 这是子的ID:>>> 17212
# 这是run函数内的number:>>> 1
# 这是参数:>>> 4
# 这是子的ID:>>> 1756
# 这是run函数内的number:>>> 1
# 这是参数:>>> 1
# 这是子的ID:>>> 6716
# 这是主进程的ID :>>> 15800
  • 然而多线程是共享的,代码如下
import time
from threading import Thread
import os

number = 0


def run(i):
    global number
    number += 1
    print(f'这是run函数内的number:>>> {number}')
    print(f'这是参数:>>> {i}')
    print(f'这是子的ID:>>> {os.getpid()}')
    time.sleep(2)

def main_thread():
    task_list = [Thread(target=run, args=(i,)) for i in range(1, 5)]
    [task.start() for task in task_list]
    [task.join() for task in task_list]
    print(f'这是主线程的ID :>>> {os.getpid()}')


if __name__ == '__main__':
    main_thread()

# 这是run函数内的number:>>> 1
# 这是参数:>>> 1
# 这是子的ID:>>> 13888
# 这是run函数内的number:>>> 2
# 这是参数:>>> 2
# 这是子的ID:>>> 13888
# 这是run函数内的number:>>> 3
# 这是参数:>>> 3
# 这是子的ID:>>> 13888
# 这是run函数内的number:>>> 4
# 这是参数:>>> 4
# 这是子的ID:>>> 13888
# 这是主线程的ID :>>> 13888

(5)查看当前进程的名字 current_thread

  • current_thread().name
import time
from threading import Thread, current_thread
import os

number = 0


def run(i):
    global number
    number += 1
    print(f'这是run函数内的number:>>> {number}')
    print(f'这是参数:>>> {i}')
    print(f'这是子的ID:>>> {os.getpid()}')
    time.sleep(2)

def main_thread():
    task_list = [Thread(target=run, args=(i,)) for i in range(1, 5)]
    [task.start() for task in task_list]
    [task.join() for task in task_list]
    print(f'这是主线程的ID :>>> {os.getpid()}')
    print(f'这是主线程的名字 :>>> {current_thread().name}')


if __name__ == '__main__':
    main_thread()
    
# 这是run函数内的number:>>> 1
# 这是参数:>>> 1
# 这是子的ID:>>> 10552
# 这是run函数内的number:>>> 2
# 这是参数:>>> 2
# 这是子的ID:>>> 10552
# 这是run函数内的number:>>> 3
# 这是参数:>>> 3
# 这是子的ID:>>> 10552
# 这是run函数内的number:>>> 4
# 这是参数:>>> 4
# 这是子的ID:>>> 10552
# 这是主线程的ID :>>> 10552
# 这是主线程的名字 :>>> MainThread

(6)守护线程 daemon

from threading import Thread
import time


def task(name):
    print(f'当前 {name} is beginning')
    time.sleep(2)
    print(f'当前 {name} is ending')


def main():
    t = Thread(target=task, args=('heart',))

    # 开启守护线程
    t.daemon = True
    t.start()

    print(f' this is main process')


if __name__ == '__main__':
    main()

# 当前 heart is beginning
#  this is main process

(7)线程的互斥锁

(1)未加锁

from threading import Thread,Lock
import time

money = 100

def task():
    global money

    # 模拟获取到车票信息
    temp = money

    # 模拟网络延迟
    time.sleep(2)

    # 模拟购票

    money = temp - 1


def main():
    t_list = []
    for i in range(5):
        t = Thread(target=task)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    # 所有子线程结束后打印 money
    print(money)


if __name__ == '__main__':
    main()
# 99

(2)加锁后

from threading import Thread,Lock
import time

money = 100

lock = Lock()
def task():
    global money

    lock.acquire()
    # 模拟获取到车票信息
    temp = money

    # 模拟网络延迟
    time.sleep(2)

    # 模拟购票

    money = temp - 1

    lock.release()


def main():
    t_list = []
    for i in range(5):
        t = Thread(target=task)
        t.start()
        t_list.append(t)

    for t in t_list:
        t.join()

    # 所有子线程结束后打印 money
    print(money)


if __name__ == '__main__':
    main()
# 95

标签:__,多线程,run,task,线程,main,ID
From: https://www.cnblogs.com/ssrheart/p/17993058

相关文章

  • C++多线程 第一章 你好,C++并发世界
    第一章你好,C++并发世界C++并发并发(concurrency):主要包括任务切换与硬件并发两类.并发(concurrency)实际上与多线程(multithreading)存在差异.并发的种类任务切换(taskswitching):计算机在某一时刻只可以真正执行一个任务,但它可以每秒切换任务许多次.通过做一......
  • 实现多线程的方式有哪几种?
    Java虚拟机时是运行所有Java程序的抽象计算机,允许应用并发的运行多个线程。在Java语言中,多线程的实现,一般有以下3中方法:1.实现Runnable接口,并实现该接口的run()方法;主要步骤:1.自定义类并实现Runnable接口,实现run()方法;2.创建Thread类,用实现Runnable接口的对象作为参数实例化......
  • python 多线程运行 串行或并行
    我们知道在python中运行多线程程序很简单,只需要几步,创建线程,start线程即可,下面简单说下多线程的串行或者并行的使用示例:#-*-coding:utf-8-*-#@Time:2024-01-2714:03importthreadingimporttimedefrun(name:str)->None:time.sleep(3)print("Thre......
  • 多线程简单介绍
    线程:是操作系统能够进行运算调度的最小单位。它被包含在进程中,是进程中实际运作单位进程:进程是程序的基本执行实体,一个程序就是一个进程简单理解线程:应用软件中相互独立,可以同时运行的功能有了多线程,就可以让程序同时做多件事线程的生命周期完整的线程状态New(新建状态)->......
  • 多线程sleep、yield、wait、join方法的使用和区别
    使用和介绍sleep方法sleep方法的官方注释(翻译后):/***根据系统计时器和调度程序的精度和准确性,使当前执行的线程休眠(暂时停止执行)指定的毫秒数。线程不会失去任何监视器的所有权(不释放锁)。**@parammillis以毫秒为单位的睡眠时间长度*@throws......
  • Python 多线程的局限性及适用场景解析
     Python是一门功能强大且广泛应用的编程语言,然而在使用多线程方面,它存在一些局限性。本文将探讨Python多线程的局限性,并分析其适用场景,帮助读者更好地理解Python多线程的实际运用。 正文: 一、Python的全局解释器锁(GIL) Python的全局解释器锁(GlobalInterpreterLock,简称GIL)是P......
  • .net 高并发(二,多线程)
    一,多线程可以通过System.Threading.Thread类来实现。下面是一个简单的示例,展示如何使用Thread类创建和管理多个线程: usingSystem; usingSystem.Threading;   classProgram { staticvoidMain() { //创建两个线程 Thre......
  • OpenMP学习 第十章 超越通用核心的多线程
    第十章超越通用核心的多线程基于通用核心的附加子句并行构造的附加子句:num_threads(integer-expression)用于设置线程总数.if(scalar-expression)用于为并行构造提供条件分支.copyin(list)proc_bind(master|close|spread)为了测试num_threads子句与if子句的用法,......
  • iOS 多线程复习
    iOS中的线程主要有四种:1.pThread2.NSThread3.GCD4.NSOpreaction基础知识:线程、任务和队列的概念: 异步、同步&并行、串行的特点:组合特点:  1.pThread C语言所写,面向过程,使用较少.oc:#pragmaMark-pThread-(void)pThreadDemo{pthread_tpthre......
  • GDB调试之多线程死锁调试(二十四)
    调试代码如下所示:#include<thread>#include<iostream>#include<vector>#include<mutex>usingnamespacestd;mutex_mutex1;mutex_mutex2;intdata1;intdata2;intdo_work_1(){ std::cout<<"线程函数do_work_1开始"<<......