接上一章,进程和线程之间可以存在哪些形式呢?

1 单进程单线程:一个人在一个桌子上吃菜。

2 单进程多线程:多个人在同一个桌子上一起吃菜。

3 多进程单线程:多个人每个人在自己的桌子上吃菜。

多线程的问题是多个人同时吃一道菜的时候容易发生争抢,例如两个人同时夹一个菜,一个人刚伸出筷子,结果伸到的时候已经被夹走菜了。。。此时就必须等一个人夹一口之后,在还给另外一个人夹菜,也就是说资源共享就会发生冲突争抢。

二、线程

threading是用来提供在一个进程内实现多线程的编程模块.

前面我们学习了多进程编程.

完成多任务, 也可以在一个进程内使用多个线程. 一个进程至少包括一个线程, 这个线程我们称之为主线程. 在主线程中开启的其他线程我们称之为子线程.

一个进程内的所有线程之间可以直接共享资源, 所以线程间的通信要比进程间通信方便了很多.

python的thread模块是比较底层的模块,python的threading模块是对thread做了一些包装的,可以更加方便的被使用

单线程示例:

import time

def say_sorry():
print("亲爱的,我错了,我能吃饭了吗?")
time.sleep(1) if __name__ == "__main__":
for i in range(5):
say_sorry()

多线程示例:

import threading
import time def say_sorry():
print("亲爱的,我错了,我能吃饭了吗?")
time.sleep(1) if __name__ == "__main__":
for i in range(5):
t = threading.Thread(target=say_sorry)
t.start() #启动线程,即让线程开始执行

说明:

  1. 可以明显看出使用了多线程并发的操作,花费时间要短很多
  2. 创建好的线程,需要调用start()方法来启动

2.1 threading

Python3 线程中常用的两个模块为:

  • _thread

    thread 模块已被废弃。用户可以使用 threading 模块代替。所以,在 Python3 中不能再使用"thread" 模块。为了兼容性,Python3 将 thread 重命名为 "_thread"

  • threading(推荐使用)

    threading用于提供线程相关的操作,线程是应用程序中工作的最小单元。

2.2 Thread类

Thread类表示在单独的控制线程中运行的活动。有两种方法可以指定这种活动:

2.2.1、给构造函数传递回调对象

mthread=threading.Thread(target=xxxx,args=(xxxx))
mthread.start()

2.2.2、在子类中重写run() 方法  这里举个小例子:

import threading
import time
class MyThread(threading.Thread):
def __init__(self,arg):
super(MyThread, self).__init__()#注意:一定要显式的调用父类的初始化函数。
self.arg=arg
def run(self):#定义每个线程要运行的函数
time.sleep(1)
print('the arg is:%s\r' % self.arg) for i in range(4):
t =MyThread(i)
t.start() print('main thread end!')

2.2.3、多线程之间共享全局变量

​ 多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响, 而多线程中,所有变量都由所有线程共享,下面观察一下两条线程中共享同一份数据。

from threading import Thread
import time g_num = 100 # 定义全局变量g_num def work1():
num = 1 # 定义局部变量num
global g_num # 关键字global标记全局变量g_num
for i in range(3):
g_num += 1 # 更改全局变量的值
print("---子线程1---work1函数---g_num:%d" % g_num) def work2():
num = 2
global g_num # 关键字global标记全局变量g_num
print("\t---子线程2---work2函数---个g_num:%d" % g_num) if __name__ == "__main__":
print("启动线程之前:g_num:%d" % g_num) t1 = Thread(target=work1) # 创建t1子线程,分配任务work1
t2 = Thread(target=work2) # 创建t2子线程,分配任务work2
t1.start() # 启动t1线程 time.sleep(1) # 等待t1线程执行完毕,观察t2线程中打印的全局变量是否发生改变
t2.start() # 启动t2线程

2.2.4、全局变量作为参数传递

将列表作为参数传递进来,在参数末尾追加元素

from threading import Thread
import time g_list = [10, 20, 30] def work1(list):
for i in range(3):
list.append(i) # 更改参数的值,在列表末尾追加元素
print("--子线程1--work1----num:", list) def work2(list):
print("\t--子线程2---work2---num:", list) if __name__ == "__main__":
print("主线程访问g_list:" , g_list) t1 = Thread(target=work1, args=(g_list,)) # 创建线程t1 将g_list作为参数传递进去 执行函数work1
t2 = Thread(target=work2, args=(g_list,)) # 创建线程t2 将g_list作为参数传递进去 执行函数work2
t1.start() time.sleep(1)
t2.start()

将列表作为参数传递进来,将参数重置

from threading import Thread
import time g_list = [10, 20 ,30] def work1(list):
for i in range(3):
# list.append(i)
list = [1, 2, 3] # 重置参数
print("--子线程1--work1----num:", list) def work2(list):
print("\t--子线程2---work2---num:", list) if __name__ == "__main__":
print("主线程访问g_list:" , g_list) t1 = Thread(target=work1, args=(g_list,)) # 创建线程t1 将g_list作为参数传递进去 执行函数work1
t2 = Thread(target=work2, args=(g_list,)) # 创建线程t2 将g_list作为参数传递进去 执行函数work2
t1.start() time.sleep(1)
t2.start()

2.2.5、线程的锁

​ 多线程和多进程最大的不同在于,多进程中,同一个变量,各自有一份拷贝存在于每个进程中,互不影响,而多线程中,所有变量都由所有线程共享,所以,任何一个变量都可以被任何一个线程修改,因此,线程之间共享数据最大的危险在于多个线程同时改一个变量,把内容给改乱了。

来看看多个线程同时操作一个变量怎么把内容给改乱了:

import time, threading

# 假定这是你的银行存款:
balance = 0 def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n def run_thread(n):
for i in range(100000):
change_it(n) if __name__ == "__main__":
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join() print(balance)

​ 每次执行的结果不一定是个啥,究其原因,是因为修改balance需要多条语句,而执行这几条语句时,线程可能中断,从而导致多个线程把同一个对象的内容改乱了。

​ 两个线程同时一存一取,就可能导致余额不对,你肯定不希望你的银行存款莫名其妙地变成了负数,所以,我们必须确保一个线程在修改balance的时候,别的线程一定不能改。

​ 如果我们要确保balance计算正确,就要给change_it()上一把锁,当某个线程开始执行change_it()时,我们说,该线程因为获得了锁,因此其他线程不能同时执行change_it(),只能等待,直到锁被释放后,获得该锁以后才能改。由于锁只有一个,无论多少线程,同一时刻最多只有一个线程持有该锁,所以,不会造成修改的冲突。创建一个锁就是通过threading.Lock()来实现:

#锁的使用
mutex = threading.Lock() #创建锁
mutex.acquire([timeout]) #锁定
mutex.release() #释放
import time, threading

balance = 0
lock = threading.Lock() def change_it(n):
# 先存后取,结果应该为0:
global balance
balance = balance + n
balance = balance - n def run_thread(n):
for i in range(100000):
# 先要获取锁:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要释放锁:
lock.release() if __name__ == "__main__":
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join() print(balance)

Python第十二章-多进程和多线程02-多线程的更多相关文章

  1. Python第十二章-多进程和多线程01-多进程

    多进程和多线程 一.进程 1.1 进程的引入 现实生活中,有很多的场景中的事情是同时进行的,比如开车的时候 手和脚共同来驾驶汽车,再比如唱歌跳舞也是同时进行的:试想,如果把唱歌和跳舞这2件事情分开依次 ...

  2. Python第十二章正则表达式(2)

    1.前提是引入import re 匹配邮箱后缀需要写入r=r'\.com\.cn|\.com|\.cn' r=r'(\w+@\w+(\.com\.con|\.com|\.cn))'ll=re.find ...

  3. Python第十二章正则表达式

    1.今天学习的f=open("d:\testcase.xml","r")会报错 需要改成f=open("d:\\testcase.xml", ...

  4. 流畅的python第十二章继承的优缺点学习记录

    子类化内置类型的缺点 多重集成和方法解析顺序 tkinter

  5. “全栈2019”Java多线程第三十二章:显式锁Lock等待唤醒机制详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  6. “全栈2019”Java多线程第二十二章:饥饿线程(Starvation)详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  7. “全栈2019”Java多线程第十二章:后台线程setDaemon()方法详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  8. 进击的Python【第十二章】:mysql介绍与简单操作,sqlachemy介绍与简单应用

    进击的Python[第十二章]:mysql介绍与简单操作,sqlachemy介绍与简单应用 一.数据库介绍 什么是数据库? 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库,每个数 ...

  9. python 教程 第二十二章、 其它应用

    第二十二章. 其它应用 1)    Web服务 ##代码 s 000063.SZ ##开盘 o 26.60 ##最高 h 27.05 ##最低 g 26.52 ##最新 l1 26.66 ##涨跌 c ...

随机推荐

  1. py基础之数据类型及基本语法

    '''python中有五种数据类型,分别是整数.浮点数.字符串.布尔值.空值'''a = 1b = 2.0c = 'hello,world'print (a,b,c)#a是整数,b是浮点数,c是字符串 ...

  2. 菜鸟系列Golang学习 — 切片

    切片简介 切片也是一种数据类型,在Golang中,切片底层基于数组实现的. 我们定义切片如下 var slice []int 切片之所以出现,是为了更好的利用资源,管理数据,如果使用数组,则我们一开始 ...

  3. Tomcat服务自动启动以隐藏start.bat命令窗口

    该方法注意先要配置好CATALINA_HOME和path等环境变量.接着主要命令有:cmd命令符下进入tomcat/bin目录,输入:service.bat install (自定义的tomcat版本 ...

  4. Netty学习(4):NIO网络编程

    概述 在 Netty学习(3)中,我们已经学习了 Buffer 和 Channel 的概念, 接下来就让我们通过实现一个 NIO 的多人聊天服务器来深入理解 NIO 的第 3个组件:Selector. ...

  5. 内网渗透之跨边界传输 - 反弹shell

    大年初一,当然是更一篇重磅文章啦 反弹shell /bin目录下带sh的都是shell nc 1.正向连接,目标机监听自身端口,攻击机主动建立连接 目标机:nc -lvvp 端口 -e /bin/ba ...

  6. Numpy之数据保存与读取

      在pandas使用的25个技巧中介绍了几个常用的Pandas的使用技巧,不少技巧在机器学习和深度学习方面很有用处.本文将会介绍Numpy在数据保存和读取方面的内容,这些在机器学习和深度学习方向也大 ...

  7. 029.核心组件-Controller Manager

    一 Controller Manager原理 1.1 Controller Manager概述 一般来说,智能系统和自动系统通常会通过一个"控制系统"来不断修正系统的工作状态.在K ...

  8. Graylog2进阶 打造基于Nginx日志的Web入侵检测分析系统

    对于大多数互联网公司,基于日志分析的WEB入侵检测分析是不可或缺的. 那么今天我就给大家讲一讲如何用graylog的extractor来实现这一功能. 首先要找一些能够识别的带有攻击行为的关键字作为匹 ...

  9. 【分布式锁】03-使用Redisson实现RedLock原理

    前言 前面已经学习了Redission可重入锁以及公平锁的原理,接着看看Redission是如何来实现RedLock的. RedLock原理 RedLock是基于redis实现的分布式锁,它能够保证以 ...

  10. Java学习笔记(2)——有关类

    Java类的高级特性: {Java管理文件机制:类包. 同一个包中的类互相访问时,可以不指定包名. 同一个包中的类不必存放在同一位置,如com.lang.class1和com.lang.class2可 ...