python 深浅拷贝案例,python浅拷贝的应用

  python 深浅拷贝案例,python浅拷贝的应用

  数字首发于微信:Python编程时间

  在并发编程中,如果多个线程访问同一个资源,必须保证不会因为数据变化而产生冲突和错误。这就是我一直所说的线程安全。

  在什么情况下访问数据是安全的?什么情况下访问数据不安全?我如何知道代码是否是线程安全的?如何保证数据的安全性?

  本文将一一解答你的疑问。

  1.不安全的线程怎么办?

  要搞清楚什么是线程安全,首先要知道线程安全是什么样的。

  例如,下面的代码打开两个线程,并将全局变量编号递增100,000次,每次递增1:

  从螺纹导入螺纹,锁紧

  数字=0

  定义目标() :

  全球号码

  for_inrange(1000000):

  数字=1

  线程_ 01=线程(目标=目标) )

  线程_ 02=线程(目标=目标) )

  线程_01.start(

  线程_02.start(

  线程_01 .加入(

  线程_02 .加入(

  是,打印(数字)

  通常我们预期的输出结果是一个线程增加100万,两个线程增加200万。输出必须是200000。

  但是,事实上,并不是你想的那样。不管执行多少次,每次输出结果都不一样。这些输出结果的特点是不到200万。

  以下是执行3次后的结果。

  1459782

  1379891

  1432921

  这种现象就是螺纹不安全。根本原因是,实际上,我们的操作数=1。正是因为不是原子操作,所以线程不安全。

  2.什么是原子操作?

  原子操作是不被线程调度机制中断的操作。一旦开始,就会一直执行到结束,中途不会切换到其他线程。

  类似于数据库中的事务。

  Python的官方文档列出了一些常见的原子操作。

  l.append(x))。

  L1。扩展器(L2))。

  x=L[i]

  x=L.pop()).

  L1=L2

  L.sort()).

  x=y

  X.字段=y

  D[x]=y

  D1。更新(D2))。

  d . key())

  接下来不是原子操作。

  i=i 1

  L.append(L[-1]).

  L[i]=L[j]

  D[x]=D[x] 1

  可以看出,像上面这样使用自增运算number=1,实际上相当于number=number 1,可以分为多个步骤。(读加法后赋值))不是原子操作。

  这样,如果多个线程同时读取,可能会读取相同的数值,两次读取只会相加一次,最终导致自增量小于预期。

  如果不知道代码是否原子,请通过dis模块的dis函数确认。

  运行这段代码时,您会看到名为number=1的行中的代码是用双字节代码实现的。

  BINARY_ADD将两个值相加。

  STORE_GLOBAL:重新代入附加值。

  每个字节码指令都是一个整体,不可分割。他达到的效果叫做原子操作。

  如果一行代码被分成多个字节码指令,在线程切换期间只能执行一个字节码指令。在这种情况下,如果代码行包含多个线程共享的变量和资源,并且多个split指令写入共享变量,就会发生数据冲突,导致数据不准确。

  为了进行比较,让我们以上面列表中的一个原子操作为例。真的如官网所说进行原子操作吗?

  这里以字典的更新操作为例。并且代码执行过程应该如下图所示。

  从截图可以看出,info.update(new)也是分几个操作的。

  加载全局变量

  LOAD_ATTR:加载属性并获取更新方法

  Load _ fast:加载新变量

  调用函数:调用函数

  POP_TOP:执行更新操作

  但是,您应该知道实际的引导数据与写操作冲突,而不是与读操作冲突。

  字典的更新方式是原子操作,因为如上所述,很多字节码指令只有一次写操作(POP_TOP)。

  3.实现人工原子操作。

  在多线程下,

  我们不能保证我们所有的代码都是原子的,所以如何让我们的代码原子化是一件非常重要的事情。

  方法也很简单,就是当大气的udon面访问一个多线程共享的资源时,加锁可以达到类似原子操作的效果。如果一个代码没有被执行,它必须在接受线程调度之前被执行。

  因此,我们使用锁定的方法来修改第一个示例,使其成为原子性的。

  从螺纹导入螺纹,锁紧

  数字=0

  lock=Lock()

  定义目标():

  全球号码

  for _ in范围(1000000):

  带锁:

  数字=1

  thread_01=线程(目标=目标)

  线程_02=线程(目标=目标)

  thread_01.start()

  thread_02.start()

  thread_01.join()

  thread_02.join()

  打印(数字)

  此时,无论执行多少次,输出都是2000000。

  4.为什么队列线程是安全的?

  Python的线程模块中有三种主要的消息通信机制:

  事件

  情况

  长队

  队列是使用最多的,我们都知道它是线程安全的。当我们向它写入和获取数据时,它不会被中断,从而导致错误,这就是为什么我们在使用队列时不需要额外的锁。

  他是怎么做到的?

  根本原因是Queue实现了lock原语,所以它可以像第三节一样实现人工原子操作。

  原语是指由若干条机器指令组成的执行特定功能的一段程序,它是不可分割的。即原语的执行必须是连续的,不允许在执行过程中被中断。

  关注微信官方账号,获取最新干货!

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: