【问题标题】:Python GIL: is django save() blocking?Python GIL:django save() 是否阻塞?
【发布时间】:2012-05-24 18:37:25
【问题描述】:

我的 django 应用程序将 django 模型保存到远程数据库。有时扑救是突发的。为了将应用程序的主线程 (*thread_A*) 从将多个对象保存到数据库的时间成本中解放出来,我考虑使用 collections.deque 将模型对象传输到单独的线程 (*thread_B*) 并拥有 * thread_B* 按顺序保存它们。

但我不确定这个方案。 save() 返回新数据库条目的 id,所以它只有在数据库响应后才“结束”,也就是事务结束。

django.db.models.Model.save() 是否真的会阻塞 GIL-wise 并在交易期间释放其他 Python 线程

【问题讨论】:

  • 您是否打算在save() 期间从任何其他线程访问数据库?
  • @DonalFellows 在我的具体情况下 - 不是本地的。一些远程观察者当然可以查询数据库,但这是正常的数据库事务使用。
  • collections.deque 线程安全吗? threading.Queue 似乎更适合这份工作。
  • collections.deque 是线程安全的(RTM)

标签: python django multithreading transactions gil


【解决方案1】:

我认为 python 不会自己锁定任何东西,但数据库会。

【讨论】:

  • 数据库是远程的,因此通过 IP 连接,所以我不确定你的意思,它不能阻塞连接,问题是本地发生了什么
  • 是的,我知道什么是 GIL,但是 Django 不使用线程,我认为它不使用 GIL。
  • django 使用数据库后端。那么这实际上是一个特定于后端的问题吗?
  • Django 使用 psycopg2 作为 Postgres 后端,而 psycopg 无法进入像数据库这样的线程,当您启动 wtite 表其锁定并且没有平均使用线程时。
  • 丹尼斯,你能改写吗,我不懂英语,因为这是我的具体情况,我有兴趣了解它
【解决方案2】:

Django 的save() 对 GIL 没有什么特别的作用。事实上,在 Python 代码中你几乎不能用 GIL 做任何事情——当它被执行时,线程必须持有 GIL。

只有两种方式可以在save() 中发布 GIL:

  • Python 决定切换线程(在sys.getcheckinterval() 指令之后)
  • Django 调用一个实现释放 GIL 的数据库接口例程

第二点可能是您要查找的内容——执行了 SQL COMMIT,在执行期间,SQL 后端释放 GIL。但是,这取决于 SQL 接口,我不确定流行的是否真的发布了 GIL*。

此外,save() 不仅仅是运行几个 UPDATE/INSERT 语句和一个 COMMIT;它在 Python 中做了大量的簿记工作,它必须保存 GIL。总而言之,我不确定将save() 移动到其他线程是否会有所收获。


更新:通过查看源代码,我了解到sqlite 模块和psycopg 在调用数据库例程时都会释放 GIL,我猜其他接口也会这样做一样。

【讨论】:

  • I/O 调用不释放 GIL 吗?真的需要明确吗?
  • 是的,确实如此。用 C 编写的例程不会自动发布 GIL,但作者必须明确这样做。见docs.python.org/c-api/…
  • 啊,好吧,我假设 SQL 接口是用 Python 编写的。
  • 感谢您发布正确答案。我想再添加一件在我的场景中引起问题的东西 [Django + mysql], db.close() db.autoCommit(True)
【解决方案3】:

通常,您不必担心 Django 应用程序中的线程。如果您使用 Apache、gunicorn 或除开发服务器之外的几乎任何其他服务器为您的应用程序提供服务,那么该服务器将产生多个进程并完全避开 GIL。例外情况是,如果您将 gunicorn 与 gevent 一起使用,在这种情况下,将有多个进程,但这些进程中还有微线程——在这种情况下,并发会有所帮助,但您不必自己管理线程即可利用那个。唯一需要担心 GIL 的情况是,如果您尝试生成多个线程来处理单个请求,这通常不是一个好主意。

Django save() 方法不会释放 GIL 本身,但数据库后端会(在大多数情况下,save() 中花费的大部分时间将用于数据库 I/O)。但是,在设计良好的 Web 应用程序中正确利用这一点几乎是不可能的。即使同步完成,您的视图中的响应也应该很快——如果他们做的工作太多而不能很快,那么使用 Celery 或其他任务主管的延迟工作来完成额外的工作。如果您尝试在视图中进行线程化,则必须在向客户端发送响应之前完成该线程,这在大多数情况下无济于事,只会增加额外的开销。

【讨论】:

  • 我的系统不是网络应用程序。我将 django 纯粹用于 ORM,不涉及 Web 服务器
  • 嗯,我明白了。好吧,如果您在 python 中进行大量处理(数据库访问之外的 CPU 时间),那么您可以使用多处理库或只启动多个进程,这就是我对使用 Django 的 ORM 的脚本所做的(我的即使有多个进程也需要几天时间才能运行)。但如果你确实使用线程,它们至少可以在数据库 i/o 操作上进行多路复用,因为实际的阻塞 i/o 将释放 GIL。
猜你喜欢
  • 2020-09-16
  • 2011-04-09
  • 2017-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-02
  • 1970-01-01
相关资源
最近更新 更多