为什么会使用线程数据竞争,但不会使用gevent

时间:2013-10-29 03:17:17

标签: python gevent

我的测试代码如下,使用线程,计数不是5,000,000,所以有数据竞争,但是使用gevent,count是5,000,000,没有数据竞争。

不是gevent coroutine执行会原子“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

1 个答案:

答案 0 :(得分:3)

复合赋值(+ =)在Python中不是原子的,gevent不会改变它。

使用gevent修补线程的计数总是5,000,000,因为gevent协程不是系统线程,gevent的上下文切换不受os控制,gevent在线程被阻塞之前不会切换(发生)。 / p>

对于普通线程,情况有所不同。复合赋值包含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