Strange python threading output

让人想犯罪 __ 提交于 2019-12-25 11:59:09

问题


I was given a simple python program to analyze. It works fine, and outputs 13, 14, and 15 randomly (of course). I can see why 13 and 14 where printed, but I don't understand where 15 came from.

Please explain.

from threading import Thread
import random
import time
import sys

def rwait():
    amt = random.uniform(0.01,0.1)
    time.sleep(amt)

x = 0

key = True

def lockx():
    global key
    while not key:
        time.sleep(0)
    rwait()
    key = False

def unlockx():
    global key
    key = True

def A():
    global x
    rwait()
    lockx()
    reg = x
    reg = reg+1
    rwait()
    x = reg
    unlockx()

def B():
    global x
    rwait()
    lockx()
    reg = x
    reg = reg+2
    rwait()
    x = reg
    unlockx()

def main():
    global x
    x = 12
    p1 = Thread(target=B)
    p1.start()
    A()
    p1.join()
    print("x=",x)


for k in range(20):
    main()

回答1:


Three different things can happen:

  • thread A and B read x before it is changed, then

    • thread A writes its result (13), and

    • thread B writes its result (14),

    and the second thread to write wins.

  • thread A or B reads x first, and writes before the other thread reads. Result: 15, as either A reads 12, adds one and writes 13, then B reads 13 and writes 15, or vice-versa.




回答2:


Your function names seem to imply they're performing locking, which they are not. This is for two reasons:

  • Accesses to key are not guaranteed atomicity.
  • Even if they were, there is a race between the time key is read and its value is True, and the time it is used and set to False.

As a result, your two threads end up modifying shared (global, in this case) state in an unsynchronised fashion. Therefore, any of three scenarios are possible:

  1. x is only incremented by 1 - B has executed wholly after x was read by A but before the incremented value was stored back.
  2. x is only incremented by 2 - same scenario as above with A and B reversed.
  3. x is incremented by 3 - A or B executes wholly before B or A, respectively.

To correctly synchronise two threads, you have to use locking. Here's an adaptation of your code, using the facilities provided by Threading:

from threading import Thread, Lock

x = 0

lock = Lock()

def lockx():
    global lock
    lock.acquire()

def unlockx():
    global lock
    lock.release()

def A():
    global x
    lockx()
    reg = x
    reg = reg+1
    x = reg
    unlockx()

def B():
    global x
    lockx()
    reg = x
    reg = reg+2
    x = reg
    unlockx()

def main():
    global x
    x = 12
    p1 = Thread(target=B)
    p1.start()
    A()
    p1.join()
    print("x=",x)

for k in range(20):
    main()



回答3:


You have demonstrated a classical concurrency problem here. Two writers act at the same time, thus potentially overwrite the data written by the other.

If you receive 13, then the thread A reads before thread B has written its result and A writes after B has written its result.

If you receive 14, then the thread B reads before thread A has written its result and B writes after A has written its result.

If you receive 15, then one thread reads (and computes and writes) after the other thread has written its result. The order of both threads cannot be determined then.

The more intriguing question is, however, why the locking mechanism (lockx/unlockx) obviously does not work. Would it work, you'd always get 15 as a result.



来源:https://stackoverflow.com/questions/19726106/strange-python-threading-output

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!