Python中的线程-我缺少什么? [英] Threading in Python - What am I missing?

查看:57
本文介绍了Python中的线程-我缺少什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我第一次尝试使用Python进行线程操作...它失败了很惨:)我想实现一个基本的关键区域问题,并发现此代码实际上并不存在问题.

This is my first attempt at threads in Python... And it failed miserably :) I wanted to implement a basic critical zone problem, and found that this code actually doesn't present a problem.

问题:为什么计数器增量没有问题?计数器运行后不应该具有随机值吗?我只能解释这种情况,如果增量已经通过原子方式执行,或者线程不是并发的.

The question: why don't I have problems with the counter increment? Shouldn't the counter have random values after a run? I can only explain this if the incrementing is already executed atomically, or if the threads are not concurrent...

import threading
import time

turnstile_names = ["N", "E", "S", "W"]
count = 0

class Counter(threading.Thread):
    def __init__(self, id):
        threading.Thread.__init__(self)
        self.id = id

    def run(self):
        global count
        for i in range(20):
            #self.sem.acquire()
            count = count + 1
            #self.sem.release()

def main():
    sem = threading.Semaphore(1)

    counters = [Counter(name) for name in turnstile_names]

    for counter in counters:
        counter.start()

    # We're running!

    for counter in counters:
        counter.join()

    print count
    return 0

if __name__ == '__main__':
    main()

注意:我留下了acquire()release()调用的注释以检查差异.我尝试在增加增量后添加小sleep来加快线程的速度-没什么区别

Notes: I left the acquire() and release() calls commented to check the difference. I tried to pace the thread adding small sleeps after the increment - no difference

解决方案/测试:感谢Kevin(请参阅下面的可接受答案).我只是在测试更改循环变量并得到以下信息:

Solution/tests: Thanks Kevin (see accepted answer below). I was just testing changing the loop variable and got this:

Loops    Result
20       99% of the time 80. Sometimes 60.
200      99% of the time 800. Sometimes 600.
2000     Maybe 10% of the time different value
20000    Finally... random numbers! I've yet to see 80000 or 60000.
         All numbers are now random, as originally expected.

我怀疑这似乎意味着线程开销约为10 ^ 4增量操作.

I suspect this seems to mean that the thread overhead is in the order of 10^4 increment operations.

另一个有趣的测试(至少在我看来,是这样):

Another interesting test (well, in my opinion, at least):

我在增量之后添加了time.sleep(random.random()/divisor)并发现,循环数再次达到20:

I added time.sleep(random.random()/divisor) after the increment and found, with the loop count again to 20:

divisor     result
100         always 4, so the race condition is always there.
1000        95% of the time 4, sometimes 3 or 5 (once 7)
10000       99% of the time NOT 4, varying from 4 to 13
100000      basically same as 10000
1000000     varying from 10 to 70
10000000... same as previous... (even with time.sleep(0))

推荐答案

如果增加每个线程的迭代次数:

If you increase the number of iterations per-thread:

def run(self):
    global count
    for i in range(100000):
        #self.sem.acquire()
        count = count + 1
        #self.sem.release()

然后确实发生了竞争情况.您的脚本打印例如175165,则预期为400000.这表明递增不是原子的.

Then a race condition does occur. Your script prints e.g. 175165, when 400000 would be expected. This suggests that incrementing is not atomic.

其他证明增量不是原子的:全局解释器锁要求CPython中线程的行为.根据维基,

Additional evidence that incrementing isn't atomic: the behavior of threads in CPython is mandated by the Global Interpreter Lock. According to the wiki,

全局解释器锁(GIL)是一个互斥体,可以防止多个本机线程一次执行Python字节码.

the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once.

如果GIL具有字节码级别的粒度,则我们期望增量不是原子的,因为要执行一个以上的字节码,如dis模块所示:

If the GIL has bytecode-level granularity, then we expect incrementing to not be atomic, because it takes more than one bytecode to execute, as demonstrated by the dis module:

>>> import dis
>>> def f():
...     x = 0
...     x = x + 1
...
>>> dis.dis(f)
  2           0 LOAD_CONST               1 (0)
              3 STORE_FAST               0 (x)

  3           6 LOAD_FAST                0 (x)
              9 LOAD_CONST               2 (1)
             12 BINARY_ADD
             13 STORE_FAST               0 (x)
             16 LOAD_CONST               0 (None)
             19 RETURN_VALUE

在这里,递增操作由字节码6至13执行.

Here, the act of incrementing is performed by byte codes 6 through 13.

那为什么原始代码不显示竞争条件?这似乎是由于每个线程的预期寿命很短-仅循环20次,每个线程将完成工作并在下一个线程开始其自己的工作之前死亡.

So why did the original code not exhibit the race condition? This seems to be due to the short life expectancy of each thread - by looping only 20 times, each thread would complete its work and die before the next thread started its own work.

这篇关于Python中的线程-我缺少什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆