python --- 线程

1. 多任务 - 线程

参考
首先考虑一个没有多任务的程序:

import timedef sing():# 唱歌 5 秒钟for i in range(5):print("-----菊花台ing....-----")time.sleep(1)def dance():# 跳舞 5秒钟for i in range(5):print("-----跳舞.....-----")time.sleep(5)def main():sing()dance()if __name__ == "__main__":main()

此时,你想同时执行唱歌、跳舞是无法做到的。如果想要同时执行,可以使用python提供的Thread来完成.

import time
from threading import Threaddef sing():# 唱歌 5 秒钟for i in range(5):print("-----菊花台ing....-----")time.sleep(1)def dance():# 跳舞 5秒钟for i in range(5):print("-----跳舞.....-----")time.sleep(1)def main():t1 = Thread(target=sing)t2 = Thread(target=dance)t1.start()t2.start()if __name__ == "__main__":main()

关键点:

  • from threading import Thread: 从threading包钟导入Thread
  • t1 = Thread(target=sing): 使用这个将函数变为线程执行的函数~

1.1 多任务的概念

上面体验了如何同时执行2个异步函数~下面补充一下多任务的概念.

简单地说,就是操作系统可以同时运行多个任务.例如: 一边逛浏览器,一遍听音乐

单核CPU执行多任务: 单核CPU执行多任务的关键在于,将cpu的时间切片. 任务1执行0.01秒,然后任务2执行0.01秒,在切到任务1执行0.01秒。由于CPU的运算速度很快,因此,我们感觉就行所有任务都在同时执行一样

多核CPU执行多任务: 真的并行执行多任务只能在多核CPU上实现. 但是,在平常的代码中,任务数量会远远的大于CPU的核心数,因此操作系统会将任务轮流调度到各个核心上执行~

并行: 同一时刻真正运行在不同的CPU上的任务

并发: 在一个很短的时间内,利用CPU的告诉运转.执行多个任务

1.2 查看当前线程数量

在某些情况下,需要查看当前程序中的线程数量,可以使用threading.enumerate()进行尝试

import threading
from time import sleep, ctimedef sing():for i in range(3):print("正在唱第%d首哥" % i)sleep(2)def dance():for i in range(3):print("正在跳第%d支舞" %i)sleep(2)if __name__ == "__main__":print("开始时间: %s" % ctime())t1 = threading.Thread(target=sing)t2 = threading.Thread(target=dance)t1.start()t2.start()while True:length = threading.enumerate()print("当前的总线程数为: %d" % length)if length <= 1:breaksleep(1)print("结束时间: %s" % ctime())

注意:

  • 当调用Thread的时候,不会创建线程
  • 当调用Thread创建出来的实例对象的 start方法时,才会创建线程以及让这个线程开始运行

1.3 创建线程的第二种方法

第一种方法是通过: t = Thread(target = 函数名)来准备, t.start()来启动

第二种方法是通过类继承threading.Thread来实现,代码如下:

import threading
import timeclass MyThread(threading.Thread):def run(self):for i in range(3):time.sleep(1)msg = "I`m " + self.name + " @" + str(i)print(msg)if __name__ == "__main__":t = MyThread()t.start()

说明:

  1. 类的继承: class MyThread(threading.Thread)
  2. 通过类的方式创建的线程,必须在该类中定义run函数. 这样当调用t.start()时候,新创建的线程会去类中寻找run函数并执行
  3. 一个t=MyThread()只会准备一个线程,当t.start()时,会创建线程~

1.4 线程共享全局变量

有些时候,多个不同的线程可能需要用到同一个变量,下面演示全局变量在多线程中的使用

from threading import Thread
import timedef work1():global g_numfor i in range(3):g_num += 1print("---- in work1, g_num is %d ---" % g_num)def work2():global g_numprint("---- in work2, g_num is %d ---" % g_num)def main():g_num = 100print("---- 线程创建执行之前 g_num is %d ---" % g_num)t1 = Thread(target=work1)t1.start()# 让 t1线程先执行time.sleep(1)t2 = Thread(target=work2)t2.start()if __name__ == "__main__":main()

注意:

  • 在一个函数中对全局变量进行修改的时候需要看是否对全局变量的指向进行了修改
    • 如果修改了指向,那么必须使用global
    • 如果仅修改了指向中的数据,则可以省略global

1.5 带参数的线程调用

在调用的时候,可能需要传递参数进去.这就需要在线程准备的时候,使用args传递参数

from threading import Thread
from time import sleepdef test1(tmp):tmp.append(33)def test2(tmp):tmp.append(66)def main():num_arr = [11, 22]print(str(num_arr))t1 = Thread(target=test1, args=(num_arr,))t2 = Thread(target=test2, args=(num_arr,))t1.start()sleep(1)print(str(num_arr))t2.start()print(str(num_arr))if __name__ == "__main__":main()

注意:

  • 多任务共享数据的原因: 多个任务合作同时完成一个大任务~
    • 一个任务获取数据
    • 一个任务处理数据
    • 一个任务发送数据

1.6 资源竞争

共享变量会产生一个资源竞争的问题: 多个线程同时对一个全局变量进行修改~下面复现问题

import threading
import timeg_num = 0def test1(num):global g_numfor i in range(num):g_num += 1print("test1: g_num: %d" % g_num)def test2(num):global g_numfor i in range(num):g_num += 1print("test2: g_num: %d" % g_num)def main():t1 = threading.Thread(target=test1, args=(1000000,))t2 = threading.Thread(target=test2, args=(1000000,))t1.start()t2.start()time.sleep(5)print("main: g_num: %d" % g_num)if __name__ == "__main__":main()

test2: g_num: 1042014
test1: g_num: 1080242
main: g_num: 1080242

以上的原因如下:

  • 假设:

    • t1代表线程1,t2代表线程2
    • g_num +=1 可分解成下面3个步骤:
      • 获取 g_num的值, 记为t1.1(t2.1)
      • 将g_num的值加1, 记为t1.2(t2.2)
      • 将加1后的值存入g_num, 记为t1.3(t2.3)
  • 下面模拟执行步骤:(根据CPU的特性,分时执行)

    • 假设先执行t1.1
    • 再执行t1.2
    • 然后执行t2.1, 此时重新获取g_num的值
    • 然后执行t1.3, 此时g_num的值并未改变

1.7 同步

以上问题可以通过线程同步来解决,在此之前,需要先了解互斥锁:

  • 当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制
  • 互斥锁未资源引入了一个状态: 锁定/非锁定
  • 某个线程要更改共享数据的时候,先将其锁定,此时资源的状态为"锁定",其他线程不能更改;知道该线程释放资源,将资源的状态变为"非锁定",其他的线程才能再次锁定该资源。互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程情况下数据的正确性

下面是互斥锁的基本使用:

# 创建锁
mutex = threading.Lock()# 锁定
mutex.acquire()# 释放
mutex.release()

注意:

  • 如果这个锁之前是没有上锁得,那么acquire不会堵塞
  • 如果在调用acquire之前已经被上锁了,那么acquire将会被阻塞直至release释放

具体做法如下:

import threading
import timedef test1(num, ):global g_num, metexfor i in range(num):metex.acquire()g_num += 1metex.release()print("test1: g_num: %d" % g_num)def test2(num, ):global g_num, metexfor i in range(num):metex.acquire()g_num += 1metex.release()print("test2: g_num: %d" % g_num)g_num = 0
metex = threading.Lock()
def main():# 创建互斥锁t1 = threading.Thread(target=test1, args=(1000000,))t2 = threading.Thread(target=test2, args=(1000000,))t1.start()t2.start()time.sleep(5)print("main: g_num: %d" % g_num)if __name__ == "__main__":main()

说明:

  • 全局变量中创建一个互斥锁: metex = threading.Lock()
  • 在原子代码前面添上: metex.acquire()
    • 原子代码: 即要么一次性全部执行,要么不执行的不可分割的代码
  • 在原子代码后面添上: metex.release()

1.8 死锁

如果两个线程分别占用一部分资源,并且同时等待对方的资源,就会造成死锁的现象。下面使用python实现一个简单的死锁程序:

import threading
import timeclass MyThread1(threading.Thread):def run(self):# 线程1 假设下面都是原子代码mutexA.acquire()print(self.name + "do1 up")time.sleep(1)mutexB.acquire()print(self.name + "do1 down")mutexB.release()mutexA.release()class MyThread2(threading.Thread):def run(self):mutexB.acquire()print(self.name + "do2 up")time.sleep(1)mutexA.acquire()print(self.name + "do2 down")mutexA.release()mutexB.release()mutexA = threading.Lock()
mutexB = threading.Lock()def main():t1 = MyThread1()t2 = MyThread2()t1.start()t2.start()if __name__ == "__main__":main()

说明:

  • 进入线程1,将mutexA锁定,然后休眠1秒
  • 进入线程2,将mutexB锁定,然后休眠1秒
  • 之后同时在线程1和2中各自获取mutexB,mutexA而进入相互等待阶段,即死锁。

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

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

相关文章

Python 链接汇总

MNIST手写识别 转载于:https://www.cnblogs.com/bycnboy/p/9095199.html

17种常用的JS正则表达式 非负浮点数 非负正数

<input typetext idSYS_PAGE_JumpPage nameSYS_PAGE_JumpPage size3 maxlength5 οnkeyupthis.valuethis.value.replace(/[^1-9]\D*$/,"") οndragenter"return false" οnpaste"return !clipboardData.getData(text).match(/\D/)"" sty…

python --- 使用conda配置pytorch

使用Conda配置PyTorch 1. 添加channels 下载地址 $ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/ $ conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/ $ conda config --add channels htt…

LDAP第三天 MySQL+LDAP 安装

https://www.easysoft.com/applications/openldap/back-sql-odbc.html OpenLDAP 使用 SQLServer 和 Oracle 数据库。 https://www.cnblogs.com/bigbrotherer/p/7251372.html          CentOS7安装OpenLDAPMySQLPHPLDAPadmin 1.安装和设置数据库 在CentOS7下&…

Myeclipse连接Mysql数据库时报错:Error while performing database login with the pro driver:unable...

driver template: Mysql connector/j&#xff08;下拉框进行选择&#xff09; driver name: 任意填&#xff0c;最好是数据库名称&#xff0c;方便查找 connection URL: jdbc:mysql://localhost:3306/programmableweb User name: 用户名 password: 密码 Driver jars: 添加jar包…

Centos6.5静态IP设置

1.创建新的虚拟机 2.打开终端&#xff0c;打开/etc/sysconfig/network-scripts/ifcfg-eth0文件 3.将BOOTPROTOstatic&#xff0c;原值为dhcp 4.添加 IPADDR192.168.43.125  #静态IP GATEWAY192.168.43.1  #网关 NETMASK255.255.255.0  #子网掩码 NETWORK192.168.43.0  …

matlab --- 图像处理基础

MATLAB图像处理 1. 数字图像处理 参考 数字图像处理(Digital Image Processing)又称为计算机图像处理,是一种将图像信号数字化利用计算进行处理的过程。随着计算机科学、电子学和光学的发展,数字图像处理已经广泛的应用到诸多领域之中。本小节主要介绍图像的概念、分类和数字…

java 注解默认值

package com.zejian.annotationdemo;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/** * Created by wuzejian on 2017/5/19. * 数据类型使用Demo */T…

[python、flask] - POST请求

1. 微信小程序POST传递数据给flask服务器 小程序端 // 提交POST数据 import { request } from "../../request/index.js"async handleDetectionPoints() {let params {url: "/detect_points",data: {"points": arr,"img_name": thi…

[vue]data数据属性及ref获取dom

data项的定义 this.$refs获取dom 获取不到数据 这样中转下才ok 小结: data里不能用this.$ref. 另外使用visjs时候 view-source:http://visjs.org/examples/network/basicUsage.html 加载不出东西,点了按钮触发才ok 小结: create里应该是从上到下执行的. 转载于:https://www.cnb…

Linux命令基础3

1. 计划任务&#xff1a;分为”一次性“ 和”长期性“ 一次性任务是由atq服务/进程来实现的&#xff0c;计划的管理操作是at命令&#xff1a; at <时间> : 安排一次性任务 atq 或at -l &#xff1a; 查看任务列表 at -c 序号&#xff1a; 预览任务与设置环境 atrm 序号…

[异步、tensorflow] - 子线程操作tensor,主线程处理tensor

参考整体流程如下图 代码 import tensorflow as tf"""模拟: 子线程不停的取数据放入队列中, 主线程从队列中取数据执行包含: 作用域的命名、把程序的图结构写入事件、多线程 """# 模拟异步存入样本. # 1、 定义一个队列,长度为1000 with tf.va…

Element

官网&#xff1a;http://element-cn.eleme.io/#/zh-CN 转载于:https://www.cnblogs.com/weibanggang/p/9995433.html

ubuntu18.04下安装Anaconda及numpy、matplotlib

为了学习深度学习&#xff0c;我需要首先掌握利用python进行科学计算的知识&#xff0c;顺便复习一下线性代数、微积分、概率论。当然&#xff0c;现在我要做的是安装Anaconda。 1、官网下载&#xff0c;linux版本&#xff1a;https://www.anaconda.com/download 2、如果太慢&a…

[tensorflow] - csv文件读取

参考 文件流程 csv读取流程 函数的流程 import tensorflow as tf import os"""tensorflow中csv文件的读取1、 先找到文件,构造一个列表2、 构造一个文件队列3、 读取(read)队列内容csv: 读取一行二进制文件: 指定一个样本的bytes读取图片文件: 按一张一张…

课程模块表结构

课程模块 我们要开始写课程模块了~~课程模块都有哪些功能呢~~ 我们的课程模块&#xff0c;包括了免费课程以及专题课程两个方向~~ 主要是课程的展示&#xff0c;点击课程进入课程详细页面~~ 课程详细页面展示&#xff0c;课程的概述&#xff0c;课程的价格策略&#xff0c;课程…

vue中computed、metfods、watch的区别

一、computed和methods 我们可以将同一函数定义为一个 method 或者一个计算属性。对于最终的结果&#xff0c;两种方式确实是相同的。 不同的是computed计算属性是基于它们的依赖进行缓存的。计算属性computed只有在它的相关依赖发生改变时才会重新求值。这就意味着只要计算依赖…

OI生涯回忆录(二)

&#xff08;二&#xff09;NOIP2016之后到HLOI2017 之后变得有点颓废&#xff0c;因为有的地方难度上来了&#xff0c;碰见不会的题我就会放挺。又或者有时候题水&#xff0c;改完了就不思进取了。到了过年前那几天连着考了几天试&#xff0c;好像是长春那边冬令营&#xff08…

[tensorflow、神经网络] - 使用tf和mnist训练一个识别手写数字模型,并测试

参考 包含: 1.层级的计算、2.训练的整体流程、3.tensorboard画图、4.保存/使用模型、5.总体代码(含详细注释) 1. 层级的计算 如上图,mnist手写数字识别的训练集提供的图片是 28 * 28 * 1的手写图像,初始识别的时候,并不知道一次要训练多少个数据,因此输入的规模为 [None, 784].…

面向过程、面向函数、面向对象的区别浅谈

Python的面向过程、面向函数、面向对象的区别浅谈 转自--猎奇古今&#xff0c;加上其他 有人之前私信问我&#xff0c;python编程有面向过程、面向函数、面向对象三种&#xff0c;那么他们区别在哪呢&#xff1f; 面向过程就是将编程当成是做一件事&#xff0c;要按步骤完成&am…