线程知识及编程

news2024/9/20 18:40:10

线程定义

在Python中,想要实现多任务还可以使用多线程来完成。

为什么使用多线程?

进程是分配资源的最小单位 , 一旦创建一个进程就会分配一定的资源 , 就像跟两个人聊QQ就需要打开两个QQ软件一样是比较浪费资源的 .

线程是程序执行的最小单位 , 实际上进程只负责分配资源 , 而利用这些资源执行程序的是线程 , 也就说进程是线程的容器 , 一个进程中最少有一个线程来负责执行程序 . 同时线程自己不拥有系统资源,只需要一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源 . 这就像通过一个QQ软件(一个进程)打开两个窗口(两个线程)跟两个人聊天一样 , 实现多任务的同时也节省了资源 .

在这里插入图片描述

多线程完成多任务

线程的创建步骤

  1. 导入线程模块

    import threading
    
  2. 通过线程类创建线程对象

    线程对象 = threading.Thread(target=任务名)
    
  3. 启动线程执行任务

    线程对象.start()
    

通过线程类创建线程对象

在这里插入图片描述

代码演示

import time
import threading


# 编写代码
def coding():
    for i in range(3):
        print("coding...")
        time.sleep(0.2)


# 听音乐
def music():
    for i in range(3):
        print("music...")
        time.sleep(0.2)


if __name__ == '__main__':
    # coding()
    # music()
    # 创建子线程
    coding_thread = threading.Thread(target=coding)
    music_thread = threading.Thread(target=music)

    # 启动子线程执行任务
    coding_thread.start()
    music_thread.start()

线程执行带有参数的任务

参数名说明
args以元组的方式给执行任务传参
kwargs以字典方式给执行任务传参
import time
import threading


# 编写代码
def coding(num):
    for i in range(num):
        print("coding...")
        time.sleep(0.2)


# 听音乐
def music(count):
    for i in range(count):
        print("music...")
        time.sleep(0.2)


if __name__ == '__main__':
    # coding()
    # music()
    # 创建子线程
    coding_thread = threading.Thread(target=coding, args=(3,))
    music_thread = threading.Thread(target=music, kwargs={"count" : 1})

    # 启动子线程执行任务
    coding_thread.start()
    music_thread.start()

线程执行带有参数的任务传参有两种方式:

1.元组方式传参 :元组方式传参一定要和参数的顺序保持一致

2.字典方式传参:字典方式传参字典中的key一定要和参数名保持一致

主线程和子线程的结束顺序

设置守护主线程

设置守护主线程的目的是主线程退出子线程销毁,不让主线程再等待子线程去执行。

设置守护主线程有两种方式:

threading.Thread(target=work, daemon=True)
线程对象.setDaemon(True)

threading.enumerate() 获取当前所有活跃的线程对象列表。使用 len() 对列表求长度可以看到当前活跃的线程的个数

主线程可以不断判断活跃线程个数,如果个数小于等于1,说明其它线程已经执行完成

import time
import threading


# 工作函数
def work():
    for i in range(10):
        print("work...")
        time.sleep(0.2)


if __name__ == '__main__':
    # 创建子线程
    # 方式1 参数方式设置守护主线程
    # work_thread = threading.Thread(target=work, daemon=True)
    work_thread = threading.Thread(target=work)
    # 启动线程
    # 方式2
    work_thread.setDaemon(True)
    work_thread.start()

    # 延时一秒
    time.sleep(1)
    print("主线程执行完毕")

线程间的执行顺序

在这里插入图片描述

线程之间执行是无序的

由CPU调度决定某个线程先执行的

import threading
import time


# 获取线程信息函数
def get_info():
    time.sleep(0.5)
    # 获取线程信息
    current_thread = threading.current_thread()
    print(current_thread)


if __name__ == '__main__':
    # 创建子线程
    for i in range(10):
        sub_thread = threading.Thread(target=get_info)
        sub_thread.start()
import threading


# 全局变量
g_num = 0


# 对g_num进行加操作
def sum_num1():
    for i in range(1000000):
        global g_num
        g_num += 1

    print("g_num1:", g_num)


# 对g_num进行加操作
def sum_num2():
    for i in range(1000000):
        global g_num
        g_num += 1

    print("g_num2:", g_num)


if __name__ == '__main__':
    # 创建子线程
    sum1_thread = threading.Thread(target=sum_num1)
    sum2_thread = threading.Thread(target=sum_num2)

    # 启动线程
    sum1_thread.start()
    sum2_thread.start()

互斥锁的使用

互斥锁: 对共享数据进行锁定,保证同一时刻只有一个线程去操作。

互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程进行等待,等锁使用完释放后,其它等待的线程再去抢这个锁。

使用

  1. 互斥锁的创建

    mutex = threading.Lock()
    
  2. 上锁

    mutex.acquire()
    
  3. 释放锁

mutex.release()

代码演示

import threading


# 全局变量
g_num = 0


# 对g_num进行加操作
def sum_num1():
    # 上锁
    mutex.acquire()

    for i in range(1000000):
        global g_num
        g_num += 1

    # 解锁
    mutex.release()

    print("g_num1:", g_num)


# 对g_num进行加操作
def sum_num2():
    # 上锁
    mutex.acquire()

    for i in range(1000000):
        global g_num
        g_num += 1

    # 解锁
    mutex.release()

    print("g_num2:", g_num)


if __name__ == '__main__':
    # 创建锁
    mutex = threading.Lock()
    # 创建子线程
    sum1_thread = threading.Thread(target=sum_num1)
    sum2_thread = threading.Thread(target=sum_num2)

    # 启动线程
    sum1_thread.start()
    sum2_thread.start()

死锁

一直等待对方释放锁的情景就是死锁。

死锁的结果:

会造成应用程序的停止响应,不能再处理其它任务了。

产生死锁的原因:没有及时或者在正确的位置释放锁

注意点:

1.使用互斥锁的时候需要注意死锁的问题,要在合适的地方注意释放锁。

2.死锁一旦产生就会造成应用程序的停止响应,应用程序无法再继续往下执行了。

import threading


# 全局变量
g_num = 0


# 对g_num进行加操作
def sum_num1():
    # 上锁
    print("sun_num1...")
    mutex.acquire()

    for i in range(1000000):
        global g_num
        g_num += 1

    print("g_num1:", g_num)


# 对g_num进行加操作
def sum_num2():
    # 上锁
    print("sun_num2...")
    mutex.acquire()

    for i in range(1000000):
        global g_num
        g_num += 1

    print("g_num2:", g_num)


if __name__ == '__main__':
    # 创建锁
    mutex = threading.Lock()
    # 创建子线程
    sum1_thread = threading.Thread(target=sum_num1)
    sum2_thread = threading.Thread(target=sum_num2)

    # 启动线程
    sum1_thread.start()
    sum2_thread.start()

进程和线程对比

关系对比

  1. 线程是依附在进程里面的,没有进程就没有线程。
  2. 一个进程默认提供一条线程,进程可以创建多个线程。

区别对比

  1. 进程之间不共享全局变量

  2. 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步

  3. 创建进程的资源开销要比创建线程的资源开销要大

  4. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位

  5. 线程不能够独立执行,必须依存在进程中

优缺点对比

1.进程—优点:可以用多核。缺点:资源开销大

2.线程—优点:资源开销小。缺点:不能使用多核

sum2_thread = threading.Thread(target=sum_num2)

# 启动线程
sum1_thread.start()
sum2_thread.start()

进程和线程对比

关系对比

  1. 线程是依附在进程里面的,没有进程就没有线程。
  2. 一个进程默认提供一条线程,进程可以创建多个线程。

区别对比

  1. 进程之间不共享全局变量

  2. 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁或者线程同步

  3. 创建进程的资源开销要比创建线程的资源开销要大

  4. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位

  5. 线程不能够独立执行,必须依存在进程中

优缺点对比

进程—优点:可以用多核。缺点:资源开销大

线程—优点:资源开销小。缺点:不能使用多核

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2035016.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业应该如何准备 EcoVadis 审核?

企业准备 EcoVadis 审核可以参考以下步骤: 注册:在网上注册并提供公司的相关信息,包括法律实体名称、国家和地区、企业规模和行业等。如果是受客户邀请参加评估,需按照邀请邮件中的链接进行注册,并确保客户能随时获知评…

【ML】强化学习(Reinforcement Learning)及其拆解

【ML】强化学习(Reinforcement Learning) 1. RL Outline 强化学习(Reinforcement Learning)概述1.1 RL的基本框架 2. RL 引入:从这个小游戏开始3. Policy Gradient 方法4. Actor-Critic 方法5. [奖励塑形(R…

(第二十六天)

上午 1、web01与web02服务器搭建 ip:10.0.0.11 systemctl stop filewalld systemctl disable firewalld setenforce 0 vim /etc/selinux/config SELINUX disabled yum -y install nginx echo "web----------01" > /usr/share/nginx/html/index.h…

力扣热题100_二叉树_230_二叉搜索树中第K小的元素

文章目录 题目链接解题思路解题代码 题目链接 230. 二叉搜索树中第K小的元素 给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 示例 1: 输入&#xff1a…

图论:欧拉路

欧拉路是什么 什么?你对这个名字感到很陌生?再看看是图论的内容,感觉是不是很难?其实一点也不难,这就是生活中的一笔画问题,也就是不重复的经过每一条边并可以访问所有的点,先看看这个图&#…

Python字符串格式化方法输出到控制台

python打印是这样的,我希望resource_id能够对齐输出: 3种格式化方法 %格式化 >>> pi3.141592653589793 >>> e2.718281828459045 >>> print("pi%.2f e%.3f" % (pi, e) ) pi3.14 e2.718“”.format()格式化 >…

操作系统(线程管理-通过条件变量实现消费者与生产者模型)

生产者与消费者模型 生产者:生产数据的线程,这类的线程负责从用户端、客户端接收数据,然后把数据Push到存储中介。 消费者:负责消耗数据的线程,对生产者线程生产的数据进行(判断、筛选、使用、响应、存储&…

x264 编码器 SSIM 算法源码分析

SSIM SSIM(Structural Similarity Index)是一种用于衡量两幅图像之间视觉相似度的指标。它不仅考虑了图像的亮度、对比度和饱和度,还考虑了图像的结构信息。SSIM的值介于-1到1之间,值越接近1表示两幅图像越相似。 SSIM是基于以下三个方面来计算的: 亮度(Luminance):比…

【Hot100】LeetCode—560. 和为 K 的子数组

目录 1- 思路前缀和 2- 实现⭐560. 和为 K 的子数组——题解思路 3- ACM 实现 原题链接:560. 和为 K 的子数组 1- 思路 前缀和 ① 借助 HashMap 存储前缀和:Key 为对应的前缀和,Value 为对应的出现次数 hm 初始化放入 (0,1) 代表和为 0 的情…

linux--chrony时间同步以及局域网同步

文章目录 注意Chrony介绍Chrony 支持的系统与联系方式Chrony Git 仓库环境准备服务端确认是否安装chrony如果没有安装,那就执行以下命令进行安装配置文件/etc/chrony.conf命令参数讲解基本用法选项命令系统时钟命令时间源命令NTP 访问命令执行命令参考 客户端确认是…

Java中等题-乘积最大子数组(力扣)

给你一个整数数组 nums ,请你找出数组中乘积最大的非空连续 子数组 (该子数组中至少包含一个数字),并返回该子数组所对应的乘积。 测试用例的答案是一个 32-位 整数。 示例 1: 输入: nums [2,3,-2,4] 输出: 6 解释: 子数组 […

Telemark电源TT-6E-BEAM SOURCE操作手侧含电路图接线

Telemark电源TT-6E-BEAM SOURCE操作手侧含电路图接线

详解LVS-dr模式

什么是LVS-dr模式 图解 在LVS-DR模式下,负载均衡器(也称为调度器)接收来自客户端的请求,并根据预设的调度算法将请求转发给后端的真实服务器(也称为RS)。真实服务器处理完请求后,直接将响应返回…

Jetpack Compose实战教程(六)

Jetpack Compose实战教程(六) 第六章 没有了margin,如何区分外边距?内边距? 文章目录 Jetpack Compose实战教程(六)一、前言二、本章目标三、开始编码3.1 特殊情况下的margin3.2 使用padding设…

基于Springboot+netty 的IM聊天室弹幕

代码地址 仓库地址 聊天室 创建springboot项目 因为我的NettyServer 是8080 所以我把服务端口改为了8081 引入netty 依赖 <dependency><groupId>io.netty</groupId><artifactId>netty-all</artifactId><version>4.1.92.Final</versi…

Prometheus+Grafana保姆笔记(1)——Prometheus+Grafana的安装

Prometheus Grafana 的组合在微服务项目中可以完成许多DevOps任务&#xff0c;它们共同提供了强大的监控和可视化功能。 我们陆续介绍Prometheus Grafana 的相关用法。 首先介绍PrometheusGrafana的安装。 安装 Prometheus Prometheus 是GO写的&#xff0c;并不依赖于 Ja…

【深度学习】注意力机制(Transformer)

注意力机制 1.基础概念 1.1 查询、键和值 在人类的注意力方式中&#xff0c;有自主性的与非自主性的注意力提示两种解释方式。所谓自主性注意力提示&#xff0c;就是人本身主动想要关注到的某样东西&#xff1b;非自主性提示则是基于环境中物体的突出性和易见性&#xff0c;…

8.13面试题目

美团商业分析实习生 一、8个球有一个重一点&#xff0c;最少称几次找出 2次 8个球中有一个重一点&#xff0c;最少称2次能找出来。 具体称重步骤如下&#xff1a; 第一次称重&#xff1a;将8个球分成三组&#xff0c;分别为3个球、3个球和2个球。将两组各3个球放在天平的两端…

【搜索二维矩阵】python刷题记录

R4-二分查找专题 直接二维变一维&#xff0c;然后二分查找就可以了 class Solution:def searchMatrix(self, matrix: List[List[int]], target: int) -> bool:nums[i for row in matrix for i in row]def binfind(the,target):low,high0,len(the)-1while low<high:mid(l…

猫罐头挑选指南!猫咪肥胖有什么危害?健康主食罐挑选攻略!

今天有位铲屎官带着一只17斤的布偶来我们医院。猫猫三岁了&#xff0c;绝育两年&#xff0c;现在因为太胖了&#xff0c;舔毛都不太方便。铲屎官说它平时不太爱动。我给体检后发现甘油三酯高&#xff0c;其他都正常&#xff0c;但必须开始减肥了&#xff0c;猫咪太胖可不好。 一…