线程的数据安全

1 数据混乱现象

from threading import Thread,Lock
num = 0
lst = [] def func1():
global num
for i in range(100000):
num -= 1 def func2():
global num
for i in range(100000):
num += 1
for i in range (10):
# 启动线程1
t1 = Thread(target=func1)
t1.start()
# 启动线程2
t2 = Thread(target=func2)
t2.start()
lst.append(t1)
lst.append(t2) for i in lst:
i.join()
print("主线程执行结束")
print(num)

执行

由于共享同一份,会出现数据混乱,如下结果

[root@node10 python]# python3 test.py
主线程执行结束
173624
[root@node10 python]# python3 test.py
主线程执行结束
131875
[root@node10 python]# python3 test.py
主线程执行结束
-4279
[root@node10 python]# python3 test.py
主线程执行结束
-93587
[root@node10 python]# python3 test.py
主线程执行结束
145643

这是因为线程是并发执行,同同一时间会有多个线程拿到同一个数据进行计算,然后再放回去,导致一个数字被多个线程修改,结果不准确

2 引入锁机制

from threading import Thread,Lock
num = 0
lst = [] def func1(lock):
global num
for i in range(100000):
#上锁修改数据
lock.acquire()
num -= 1
#解锁释放
lock.release() def func2(lock):
global num
for i in range(100000):
#使用with,自动上锁和解锁
with lock:
num += 1
lock = Lock()
for i in range (10):
# 启动线程1
t1 = Thread(target=func1,args=(lock,))
t1.start()
# 启动线程2
t2 = Thread(target=func2,args=(lock,))
t2.start()
lst.append(t1)
lst.append(t2) for i in lst:
i.join()
print("主线程执行结束")
print(num)

执行

[root@node10 python]# python3 test.py
主线程执行结束
0

结果正确,但是消耗的时间毕竟长,但是可以换取数据正确

3 信号量

import time,random,os

def func(i,sem):
time.sleep(random.uniform(0.1,1))
with sem:
print (i)
print (time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
time.sleep(random.uniform(3,6)) sem = Semaphore(5)
for i in range (20):
Thread(target = func,args=(i,sem)).start()

执行

[root@node10 python]# python3 test.py
3
2020-02-23 03:55:24
5
2020-02-23 03:55:24
14
2020-02-23 03:55:24
18
2020-02-23 03:55:24
6
2020-02-23 03:55:24
0
2020-02-23 03:55:27
2
2020-02-23 03:55:29
15
2020-02-23 03:55:29
1
2020-02-23 03:55:29
19
2020-02-23 03:55:30
12
2020-02-23 03:55:32
4
2020-02-23 03:55:32
17
2020-02-23 03:55:34
16
2020-02-23 03:55:34
13
2020-02-23 03:55:35
7
2020-02-23 03:55:36
10
2020-02-23 03:55:36
8
2020-02-23 03:55:39
9
2020-02-23 03:55:39
11
2020-02-23 03:55:40

信号量就相当于同时可以上多把锁

死锁,递归锁,互斥锁

4 死锁现象

模拟一个俱乐部玩枪,两把枪,两盒子弹,四个人同时抢,只有同时抢到子弹和枪的人,才能玩枪

初步逻辑

from threading import Thread,Lock
import time
#首先创建两把锁
gun = Lock()
bullet = Lock()
#定义第一种,先抢到枪,再抢到子弹
def play1(name):
#抢到枪,上锁
gun.acquire()
print("%s拿到枪"%(name))
#抢到子弹上锁
bullet.acquire()
print ("%s抢到子弹"%(name))
print ("开枪玩一会") time.sleep(0.5)
#放下枪,解锁
gun.release()
print("%s放下枪"%(name))
#放下子弹,解锁
bullet.release()
print("%s放下子弹"%(name)) #定义第二种,先抢到子弹,再抢到枪
def play2(name):
#抢到子弹上锁
bullet.acquire()
print ("%s抢到子弹"%(name))
#抢到枪,上锁
gun.acquire()
print("%s拿到枪"%(name))
print ("开枪玩一会")
time.sleep(0.5)
#放下子弹,解锁
bullet.release()
print("%s放下子弹"%(name))
#放下枪,解锁
gun.release()
print("%s放下枪"%(name)) name_lst1 = ["John","Jim"]
name_lst2 = ["Tom","Jerry"]
for name in name_lst1:
Thread(target=play1,args=(name,)).start()
for name in name_lst2:
Thread(target=play2,args=(name,)).start()

这种情况就会有死锁现象

多次执行,结果如下

[root@node10 python]# python3 test.py
John拿到枪
Tom抢到子弹
#阻塞,是因为John和Tom同时上了枪锁和子弹锁,但是都没有解锁,造成死锁
[root@node10 python]# python3 test.py
John拿到枪
John抢到子弹
开枪玩一会
John放下枪
John放下子弹
Jim拿到枪
Tom抢到子弹
#阻塞,这次是John顺利的玩一局,但是到Jim和Tom同时抢到枪和子弹,上锁,但是没有解锁,造成死锁
[root@node10 python]# python3 test.py
John拿到枪
John抢到子弹
开枪玩一会
John放下枪
John放下子弹
Tom抢到子弹
Jim拿到枪
#阻塞

5 递归锁介绍

上几把锁,解几把锁

from threading import Thread,RLock
rlock = RLock()
def func(name):
rlock.acquire()
print(name,1)
rlock.acquire()
print(name,2)
rlock.acquire()
print(name,3)
rlock.release()
rlock.release()
rlock.release() for i in range(10):
t = Thread(target=func,args=("name%s" % (i),) )
t.start() print("程序执行结束")

执行

[root@node10 python]# python3 ceshi.py
name0 1
name0 2
name0 3
name1 1
name1 2
name1 3
name2 1
name2 2
name2 3
name3 1
name3 2
name3 3
程序执行结束
name4 1
name4 2
name4 3
name5 1
name5 2
name5 3
name6 1
name6 2
name6 3
name7 1
name7 2
name7 3
name8 1
name8 2
name8 3
name9 1
name9 2
name9 3

6 利用递归锁,解决死锁现象

临时用于快速解决服务器崩溃死锁的问题,用递归锁应急问题

from threading import Thread,RLock
import time
#首先创建递归锁
gun=bullet = RLock()
#定义第一种,先抢到枪,再抢到子弹
def play1(name):
#抢到枪,上锁
gun.acquire()
print("%s拿到枪"%(name))
#抢到子弹上锁
bullet.acquire()
print ("%s抢到子弹"%(name))
print ("开枪玩一会") time.sleep(0.5)
#放下枪,解锁
gun.release()
print("%s放下枪"%(name))
#放下子弹,解锁
bullet.release()
print("%s放下子弹"%(name)) #定义第二种,先抢到子弹,再抢到枪
def play2(name):
#抢到子弹上锁
bullet.acquire()
print ("%s抢到子弹"%(name))
#抢到枪,上锁
gun.acquire()
print("%s拿到枪"%(name))
print ("开枪玩一会")
time.sleep(0.5)
#放下子弹,解锁
bullet.release()
print("%s放下子弹"%(name))
#放下枪,解锁
gun.release()
print("%s放下枪"%(name)) name_lst1 = ["John","Jim"]
name_lst2 = ["Tom","Jerry"]
for name in name_lst1:
Thread(target=play1,args=(name,)).start()
for name in name_lst2:
Thread(target=play2,args=(name,)).start()

执行

[root@node10 python]# python3 test.py
John拿到枪
John抢到子弹
开枪玩一会
John放下枪
John放下子弹
Jim拿到枪
Jim抢到子弹
开枪玩一会
Jim放下枪
Jim放下子弹
Tom抢到子弹
Tom拿到枪
开枪玩一会
Tom放下子弹
Tom放下枪
Jerry抢到子弹
Jerry拿到枪
开枪玩一会
Jerry放下子弹
Jerry放下枪

执行成功,神奇

7 使用互斥锁解决

从语法上来看,锁是可以互相嵌套的,但是不要使用

上一次锁,就对应解开一把锁,形成互斥锁

拿枪和拿子弹是同时的,上一把锁就够了,不要分开上锁,也不要去写锁的嵌套,容易死锁

from threading import Thread,Lock
import time
#首先创建递归锁
mylock = Lock()
#定义第一种,先抢到枪,再抢到子弹
def play1(name):
#抢到枪和子弹,上锁
mylock.acquire()
print("%s拿到枪"%(name))
print ("%s抢到子弹"%(name)) print ("开枪玩一会")
time.sleep(0.5)
#放下枪,和子弹解锁
print("%s放下枪"%(name))
print("%s放下子弹"%(name))
mylock.release() #定义第二种,先抢到子弹,再抢到枪
def play2(name):
#抢到枪子弹上锁
mylock.acquire()
print ("%s抢到子弹"%(name))
print("%s拿到枪"%(name)) print ("%s开枪玩一会"%(name))
time.sleep(0.5)
#放枪下子弹,解锁
print("%s放下子弹"%(name))
print("%s放下枪"%(name))
mylock.release() name_lst1 = ["John","Jim"]
name_lst2 = ["Tom","Jerry"]
for name in name_lst1:
Thread(target=play1,args=(name,)).start()
for name in name_lst2:
Thread(target=play2,args=(name,)).start()

执行

[root@node10 python]# python3 test.py
John拿到枪
John抢到子弹
开枪玩一会
John放下枪
John放下子弹
Jim拿到枪
Jim抢到子弹
开枪玩一会
Jim放下枪
Jim放下子弹
Tom抢到子弹
Tom拿到枪
Tom开枪玩一会
Tom放下子弹
Tom放下枪
Jerry抢到子弹
Jerry拿到枪
Jerry开枪玩一会
Jerry放下子弹
Jerry放下枪

完成

044.Python线程的数据安全的更多相关文章

  1. python——线程与多线程进阶

    之前我们已经学会如何在代码块中创建新的线程去执行我们要同步执行的多个任务,但是线程的世界远不止如此.接下来,我们要介绍的是整个threading模块.threading基于Java的线程模型设计.锁( ...

  2. python——线程与多线程基础

    我们之前已经初步了解了进程.线程与协程的概念,现在就来看看python的线程.下面说的都是一个进程里的故事了,暂时忘记进程和协程,先来看一个进程中的线程和多线程.这篇博客将要讲一些单线程与多线程的基础 ...

  3. [python] 线程简介

    参考:http://www.cnblogs.com/aylin/p/5601969.html 我是搬运工,特别感谢张岩林老师! python 线程与进程简介 进程与线程的历史 我们都知道计算机是由硬件 ...

  4. PYTHON线程知识再研习A

    前段时间看完LINUX的线程,同步,信息号之类的知识之后,再在理解PYTHON线程感觉又不一样了. 作一些测试吧. thread:模块提供了基本的线程和锁的支持 threading:提供了更高级别,功 ...

  5. Python 线程(threading) 进程(multiprocessing)

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  6. Python线程:线程的调度-守护线程

    Python线程:线程的调度-守护线程   守护线程与普通线程写法上基本么啥区别,调用线程对象的方法setDaemon(true),则可以将其设置为守护线程.在python中建议使用的是thread. ...

  7. python 线程(一)理论部分

    Python线程 进程有很多优点,它提供了多道编程,可以提高计算机CPU的利用率.既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的. 主要体现在一下几个方面: 进程只能在 ...

  8. python线程同步原语--源码阅读

    前面两篇文章,写了python线程同步原语的基本应用.下面这篇文章主要是通过阅读源码来了解这几个类的内部原理和是怎么协同一起工作来实现python多线程的. 相关文章链接:python同步原语--线程 ...

  9. Python学习——Python线程

    一.线程创建 #方法一:将要执行的方法作为参数传给Thread的构造方法 import threading import time def show(arg): time.sleep(2) print ...

随机推荐

  1. STM8 ADC 多个通道连续扫描缓冲区数据带中断模式的正确写法

    近日调试了STM8S的ADC采集多通道数据的程序,按照之前的立即,将ADC1设置为:扫描模式,连续采集,数据缓存模式,中断使能后应该可以在中断后读取到数值了,可是无论怎样都只能读取到第一个缓冲器的数据 ...

  2. 什么是 ThreadLocal?

    什么是 ThreadLocal? ThreadLocal 诞生于 JDK 1.2,用于解决多线程间的数据隔离问题.也就是说 ThreadLocal 会为每一个线程创建一个单独的变量副本. Thread ...

  3. 10个很多人不知道的Redis使用技巧

    前言 Redis 在当前的技术社区里是非常热门的.从来自 Antirez 一个小小的个人项目到成为内存数据存储行业的标准,Redis已经走过了很长的一段路.随之而来的一系列最佳实践,使得大多数人可以正 ...

  4. 硬件原理系列之LED数码管(一)

    LED数码管也叫数码显示器,由8段(7段,8多一位小数点)发光二极管组成,控制不同组合,就可以显示不同字符 dp示小数点,COM为公共端,根据连接方式的不同,分为共阴极和共阳极 工作原理:若选用共阴极 ...

  5. flink 自定义触发器 定时或达到数量触发

    flink 触发器 触发器确定窗口(由窗口分配程序形成)何时准备由窗口函数处理.每个WindowAssigner都带有一个默认触发器. 如果默认触发器不适合需求,我们就需要自定义触发器. 主要方法 触 ...

  6. 七、Application类

    前言:每个运行的WPF应用程序都由System.Windows.Application类的一个实例来表示.程序集资源(assemblyre sources)的每个资源是一块可嵌入到编译过得应用程序中的 ...

  7. webdriver的常用方法

    webdriver的常用方法 click():点击元素 clear():清除文本 send_keys(value):模拟按键输入 # -*- coding:utf-8 -*- from seleniu ...

  8. 一台电脑上配置多个git的ssh key

    前几天公司的代码库全部迁移到了阿里云上,在配置git的ssh key的时候遇到了一个问题,那就是自己的密钥在添加时提示已经存在,原来是自己的个人账号上已经添加过这个密钥了,公司分配的账号就不能再添加这 ...

  9. 题解 SDOI2010 【栗栗的书架】

    \[ Preface \] 看到这题洛谷标签有 主席树 ,还以为是什么二维主席树的玄学做法(雾 \[ Description \] 给出一个 \(R×C\) 的矩阵. 一共 \(m\) 次询问,每次询 ...

  10. java架构之路-(微服务专题)feign的基本使用和nacos的配置中心

    上次回归: 上次我们说了ribbon的基本使用,包括里面的内部算法,算法的细粒度配置,还有我们自己如何实现我们自己的算法,主要还是一些基本使用的知识,还不会使用ribbon的小伙伴可以回去看一下上一篇 ...