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而进入相互等待阶段,即死锁。

python --- 线程相关推荐

  1. python3 线程池源码解析_5分钟看懂系列:Python 线程池原理及实现

    概述 传统多线程方案会使用"即时创建, 即时销毁"的策略.尽管与创建进程相比,创建线程的时间已经大大的缩短,但是如果提交给线程的任务是执行时间较短,而且执行次数极其频繁,那么服务器 ...

  2. 什么是Python线程?Python线程如何创建?

    相信正在学习Python技术或者对Python语言有一定了解的人对于Python线程应该都不陌生,但是也有刚接触Python的小伙伴对于Python线程并不了解,今天小编就跟大家聊聊什么是Python ...

  3. python 线程锁 共享全局变量 线程通信

    python 线程锁 共享全局变量 线程通信 注意:全局变量不必做为参数传到函数里!!! import threading # 银行存钱和取钱 # 存钱1万次 def add():global mon ...

  4. python线程池阻塞队列_福利又来啦!python多线程进阶篇

    使用Python中的线程模块,能够同时运行程序的不同部分,并简化设计.如果你已经入门Python,并且想用线程来提升程序运行速度的话,希望这篇教程会对你有所帮助. 通过阅读本文,你将了解到:什么是死锁 ...

  5. python - 线程

    python之路--线程 简介 操作系统线程理论 线程概念的引入背景 线程的特点 进程和线程的关系 使用线程的实际场景 用户级线程和内核级线程(了解) 线程和python 理论知识 线程的创建Thre ...

  6. Python线程同步机制: Locks, RLocks, Semaphores, Condition

    为什么80%的码农都做不了架构师?>>>    翻译自Laurent Luce的博客 原文名称:Python threads synchronization: Locks, RLoc ...

  7. php 线程锁,如何使用python线程锁(实例解析)

    在这篇文章之中我们来了解一下什么是python线程锁.了解一下python线程锁的相关知识,以及线程锁在python编程之中能起到什么样的作用. 线程锁(互斥锁Mutex) 一个进程下可以启动多个线程 ...

  8. Python线程、进程知识整理

    一.python线程 Threading用于提供线程相关的操作,线程是应用程序中工作的最小单元. 1 #!/usr/bin/env python2 # -*- coding:utf-8 -*-3 im ...

  9. python线程暂停_关于多线程:如何使“停止”按钮终止已经在Tkinter中运行的“开始”功能(Python)...

    我正在使用带有两个主要按钮的Tkinter制作GUI:"开始"和"停止". 您能否为以下代码提供建议,以使"停止"按钮终止由"开始 ...

  10. Python 线程和进程和协程总结

    Python 线程和进程和协程总结 线程和进程和协程 进程 进程是程序执行时的一个实例,是担当分配系统资源(CPU时间.内存等)的基本单位: 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其 ...

最新文章

  1. Python报错:NameError: name ‘wraps‘ is not defined
  2. vc++从txt文件中读取数据
  3. 人工智能写散文之躲进你的心里记录温暖的你
  4. 简单超级组计划 打造强悍手臂
  5. 小氓男-灰色按钮激活V1.0
  6. java学习(148):三个参数的输入流
  7. Spring操作Redis
  8. mysql srid_mysql8 参考手册--索引优化
  9. C# DllImport的用法(转)
  10. 工作那些事儿(8)- 废旧立新
  11. bianma 水平 技巧
  12. 【转】Android Studio安装配置学习教程指南 Gradle基础--不错
  13. 实际返回的行数超出请求的行数_代码行数越少越好?
  14. (转)智能投顾的中国特色:强需求,大规模,低门槛与巨大提升空间
  15. Excel如何批量添加批注
  16. Python 中文数字对照表 输入一个数字,转换成中文数字。比如:1234567890 -> 壹贰叁肆伍陆柒捌玖零。【简单易懂,代码可以直接运行】
  17. 想要提高自己的写作水平?吃透这篇文章就够了
  18. 下载stm32f10x标准外设库
  19. mysql创建触发器
  20. 看完富爸爸穷爸爸的感悟

热门文章

  1. android 字体竖直居中_问下弹性盒内不知道高度的时候想让字体垂直居中代码要怎么写...
  2. python模块管理工具,Python的包管理工具
  3. asp.net mvc项目实例_降龙-第13章:MVC开发准备
  4. linux安装icc步骤,怎麼安装不到 icc?
  5. 富文本++php+源码,自己实现富文本编辑器
  6. lr监控mysql_LR通过SiteScope监控mysql
  7. java web 导出word_JavaWeb Project使用FreeMaker导出Word文件
  8. 团队行为心理学读书笔记(7)团队激励背后的行为心理学
  9. 求生2本地服务器怎么修改参数,《求生之路2》服务器指令及难度参数设置(难度篇)...
  10. 安装好hadoop集群后,报错如下n org.apache.hadoop.ipc.RemoteException(java.io.IOException): File /data/hadoop-roo