Блокировка не блокирует мою переменную так, как я хочу - PullRequest
0 голосов
/ 24 марта 2019

Я пытаюсь увеличить переменную с помощью многопоточности.Однако, когда я запускаю код, счетчик остается на 1. Когда я удаляю второй сон, он работает как обычно (с шагом 5), однако я не могу понять, как правильно заблокировать переменную.

Я уже пытался заблокировать переменную до создания tmp, а также другие методы блокировки (с блокировкой, try-finalize, ...).

class Casino:
    euro = 0


class PlayingThread(threading.Thread):

    def __init__(self, the_casino, playerno=1):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.playerno = playerno
        self.the_casino = the_casino

    def run(self):
        time.sleep(2)
        tmp = self.the_casino.euro
        time.sleep(1)
        self.lock.acquire()
        self.the_casino.euro = tmp + 1
        self.lock.release()


casino = Casino()
lt = []
for i in range(0, 5):
    pt = PlayingThread(casino, i)
    pt.start()
    lt.append(pt)

for t in lt:
    t.join()

print("We earned a lot of money! Sum=", casino.euro)

Ожидаемый результат будет «Мы заработали».. Sum = 5 "но это" ... Sum = 1 "

Ответы [ 3 ]

0 голосов
/ 24 марта 2019

Попробуйте это

class Casino:
euro = 0


class PlayingThread(threading.Thread):

    def __init__(self, the_casino, playerno=1):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.playerno = playerno
        self.the_casino = the_casino

    def run(self):
        try:
            self.lock.acquire()
            self.the_casino.euro += 1
        finally:
            self.lock.release()


casino = Casino()
lt = []
for i in range(0, 5):
    pt = PlayingThread(casino, i)
    pt.start()
    lt.append(pt)

for t in lt:
    t.join()

print("We earned a lot of money! Sum=", casino.euro)

Проблема в том, что вы на самом деле не увеличиваете casino.euro, оно всегда равно 0 и присваивается tmp.

0 голосов
/ 25 марта 2019

У вас есть две ошибки.Первое упомянуто в ответе Соломона Слоу.Вы не удерживаете блокировку в течение всей операции чтения-изменения-записи.Как вы и предполагали, это можно исправить, передвинув блокировку вверх.

Но у вас есть другая проблема - никакая блокировка не защищает euro.Каждый поток блокирует сам , позволяя каждому потоку захватить блокировку для себя и одновременно выполнять чтение-изменение-запись.

Чтобы исправить это, необходима специальная блокировка, которая защищаетeuro и все операции над ним должны выполняться под защитой этого единственного замка.Для этого я добавил блокировку к Casino.

Вот исправленный код:

import threading
import time

class Casino:
    euro = 0
    lock = threading.Lock();


class PlayingThread(threading.Thread):

    def __init__(self, the_casino, playerno=1):
        threading.Thread.__init__(self)
        self.lock = threading.Lock()
        self.playerno = playerno
        self.the_casino = the_casino

    def run(self):
        time.sleep(2)
        self.the_casino.lock.acquire()
        tmp = self.the_casino.euro
        time.sleep(1)
        self.the_casino.euro = tmp + 1
        self.the_casino.lock.release()


casino = Casino()
lt = []
for i in range(0, 5):
    pt = PlayingThread(casino, i)
    pt.start()
    lt.append(pt)

for t in lt:
    t.join()

print("We earned a lot of money! Sum=", casino.euro)

Я думаю, вы можете упустить что-то фундаментальное в том, как работают блокировки.Блокировка не имеет представления о том, что она блокирует, и не препятствует ничему, кроме двух потоков, одновременно удерживающих одну и ту же блокировку.Вы хотите убедиться, что ни один поток не может прочитать euro между другими потоками, которые читают euro, увеличивают значение чтения и записывают обратно в него.Способ сделать это - обеспечить, чтобы каждый поток, который каким-либо образом мог взаимодействовать с euro, содержал одну конкретную блокировку.

Когда мы говорим, что какая-то конкретная блокировка защищает определенный фрагмент данных, то, что мыЭто означает, что ни один поток не пытается получить доступ или изменить этот конкретный фрагмент данных без удержания этой конкретной блокировки.Очевидно, для этого требуется, чтобы код был тщательно сконструирован для соответствия этому требованию.

0 голосов
/ 24 марта 2019

Все ваши темы читают переменную, прежде чем они ее заблокируют

    tmp = self.the_casino.euro

Второй сон гарантирует, что все потоки успеют увидеть self.the_casino.euro равным нулю, прежде чем любой из них изменит его.

Затем, после того, как они проснулись, каждый из них устанавливает его на tmp + 1 (т.е. каждый из них устанавливает его на 1).


Если вы хотите получить 5, то вам нужно сделать чтение и обновление переменной одной атомарной операцией. Это можно сделать, поместив чтение и обновление в один и тот же критический раздел.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...