我的测试代码如下,使用线程,count不是5,000,000,所以出现了数据竞争,但是使用gevent,count是5,000,000,没有数据竞争。
是不是gevent协程执行时会原子“count+=1”,而不是拆分成一条CPU指令来执行?
# -*- coding: utf-8 -*-
import threading
use_gevent = True
use_debug = False
cycles_count = 100*10000
if use_gevent:
from gevent import monkey
monkey.patch_thread()
count = 0
class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
super(Counter, self).__init__(name=name)
def run(self):
global count
for i in xrange(cycles_count):
if use_debug:
print '%s:%s' % (self.thread_name, count)
count = count + 1
counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
counter.start()
for counter in counters:
counter.join()
print 'count=%s' % count
Run Code Online (Sandbox Code Playgroud)
复合赋值(+=)在 Python 中不是原子的,gevent 不会改变这一点。
使用 gevent 补丁线程的计数始终为 5,000,000,因为 gevent 协程不是系统线程,gevent 的上下文切换不受操作系统控制,并且 gevent 不会切换,直到线程被阻塞(io 发生)。
对于普通线程,情况有所不同。复合赋值包含 3 个步骤(a. 读取值,b. 增加值,c. 分配更改的值),任何这些步骤之间都可能发生线程切换。
检查下面的代码,我添加了一个将运行无限循环的新线程。对于普通线程,可以打印count,因为os会自动切换线程。但是对于gevent线程,count不会被打印出来,因为无限线程一旦执行,就永远不会结束,gevent也不会因为没有IO发生而切换到其他线程。
# -*- coding: utf-8 -*-
import threading
use_gevent = True
use_debug = False
cycles_count = 100
if use_gevent:
from gevent import monkey
monkey.patch_thread()
count = 0
class Counter(threading.Thread):
def __init__(self, name):
self.thread_name = name
super(Counter, self).__init__(name=name)
def run(self):
global count
for i in xrange(cycles_count):
if use_debug:
print '%s:%s' % (self.thread_name, count)
if use_gevent:
print self.thread_name
count = count + 1
class Infinite(threading.Thread):
def run(self):
if use_gevent:
print 'Infinite ...'
while True:
pass
counters = [Counter('thread:%s' % i) for i in range(5)]
for counter in counters:
counter.start()
infinite = Infinite()
infinite.start()
for counter in counters:
counter.join()
print 'count=%s' % count
Run Code Online (Sandbox Code Playgroud)