【问题标题】:Memory Shared with threading与线程共享的内存
【发布时间】:2021-02-18 17:08:12
【问题描述】:

我在尝试链接线程内存时遇到问题。我希望计数器在线程之间共享内存,所有线程都只计数到某个数字(在这种情况下为 100),最后它返回到主线程。问题是即使有锁所有线程也只有一个计数

import threading
from threading import Thread, Lock
import time
import multiprocessing
import random

def create_workers(n_threads, counter):
    # counter = 0
    workers = []
    for n in range(n_threads):
        worker = DataCampThread('Thread - ' + str(n), counter)
        workers.append(worker)

    for worker in workers:
        worker.start()

    for worker in workers:
        worker.join()

    return counter

def thread_delay(thread_name, num, delay):
    num += 1
    time.sleep(delay)
    print(thread_name, '-------->', num)
    return num

class DataCampThread(Thread):
    def __init__(self, name, cou):
        Thread.__init__(self)
        self.name = name
        self.counter = cou
        delay = random.randint(1, 2)
        self.delay = delay
        self.lock = Lock()

    def run(self):
        print('Starting Thread:', self.name)
        while self.counter < 100:
            self.lock.acquire()
            self.counter = thread_delay(self.name, self.counter, self.delay)
            self.lock.release()
        print('Execution of Thread:', self.name, 'is complete!')

if __name__ == '__main__':
    # create the agent
    n_threads = 3#multiprocessing.cpu_count()
    counter = 0
    create_workers(n_threads, counter)
    print(counter)
    print("Thread execution is complete!")

【问题讨论】:

  • 当我运行当前问题中的代码时,所有线程似乎都有自己的计数并在不同的时间完成 - 所以不清楚问题是什么。 注意没有理由将最大值设置为 100,一个更小的值可以用于调试目的。
  • 还不清楚为什么要使用Lock,因为每个DataCampThread 都有自己的计数器(因此多个线程永远不会更新任何共享内存)。
  • 主要思想是只有一个计数,而不是每个线程有不同的计数。我认为每个线程都对异步主计数做出贡献
  • 在这种情况下,您可能希望拥有一个全局共享的count 值,每个线程都会更新该值(在获取锁之后)。我会尝试向您展示如何做到这一点,但我并不真正了解正在发生的一切。定义自己的 Thread 子类的目的/目标到底是什么(而不是仅仅编写一个函数并将其作为内置 Thread 类的参数传递)?
  • 也想知道为什么thread_delay() 函数不是类的方法?

标签: python multithreading memory shared


【解决方案1】:

正如我在 cmets 中提到的,我不太确定您要做什么 - 但这是一个不知情的猜测(希望)加快事情的发展。

根据您对我关于希望避免使用全局变量的回答的初始版本的回复,计数器现在是一个类属性,将自动由该类的所有实例共享。每个线程都有自己的名称和随机选择的更新共享类属性counter 之间的延迟时间。

注意:测试代码重新定义了print()函数,以防止它一次被多个线程使用。

import threading
from threading import Thread, Lock
import time
import random

MAXVAL = 10


class DataCampThread(Thread):

    counter = 0  # Class attribute.
    counter_lock = Lock()  # Control concurrent access to shared class attribute.

    def __init__(self, name):
        super().__init__()  # Initialize base class.
        self.name = name
        self.delay = random.randint(1, 2)

    def run(self):
        print('Starting Thread:', self.name)
        while True:
            with self.counter_lock:
                if self.counter >= MAXVAL:
                    break  # Exit while loop (also releases lock).
#                self.counter += 1  # DON'T USE - would create an instance-level attribute.
                type(self).counter += 1  # Update class attribute.
                print(self.name, '-------->', self.counter)
            time.sleep(self.delay)
        print('Execution of Thread:', self.name, 'is complete!')


def main(n_threads, maxval):
    ''' Create and start worker threads, then wait for them all to finish. '''

    workers = [DataCampThread(name=f'Thread #{i}')  for i in range(n_threads)]

    for worker in workers:
        worker.start()

    # Wait for all treads to finish.
    for worker in workers:
        worker.join()


if __name__ == '__main__':

    import builtins

    def print(*args, **kwargs):
        ''' Redefine print to prevent concurrent printing. '''
        with print.lock:
            builtins.print(*args, **kwargs)

    print.lock = Lock()  # Function attribute.

    n_threads = 3
    main(n_threads, MAXVAL)
    print()
    print('Thread execution is complete!')
    print('final counter value:', DataCampThread.counter)

样本输出:

Starting Thread: Thread #0
Starting Thread: Thread #1
Thread #0 --------> 1
Starting Thread: Thread #2
Thread #1 --------> 2
Thread #2 --------> 3
Thread #1 --------> 4
Thread #0 --------> 5
Thread #2 --------> 6
Thread #2 --------> 7
Thread #1 --------> 8
Thread #0 --------> 9
Thread #2 --------> 10
Execution of Thread: Thread #1 is complete!
Execution of Thread: Thread #0 is complete!
Execution of Thread: Thread #2 is complete!

Thread execution is complete!
final counter value: 10

【讨论】:

  • 谢谢你的代码,我明白了一点。不过,我希望有一个不是全局的共享变量。我不知道这是否可能。
  • 确实,共享的非全局变量确实听起来有点矛盾。但是,我认为一种解决方法是使用所有实例都可以访问的类级属性 - 请参阅更新的答案。
  • 非常感谢,这就是我想做的。一个问题,为什么要用函数 type() 和 counter 变量
  • 几个原因。 1.) 所以它更新了 class 属性。 2.) 避免将类名硬编码到方法的代码中(即DataCampThread.counter += 1)——这意味着它会自动继续工作,而不管当前类的名称是什么。
  • 其实还有一个(复杂的)第三个原因。改用self.counter += 1 会引入一个错误,因为它会产生创建名为counter 的实例级属性的副作用,从那时起,该属性将隐藏具有相同名称的类级属性。这是由于一个神秘的规则……在下一条评论中继续
猜你喜欢
  • 2020-02-23
  • 1970-01-01
  • 2010-12-09
  • 2014-04-19
  • 2016-03-01
  • 1970-01-01
  • 2014-10-08
  • 2012-07-29
  • 2012-10-31
相关资源
最近更新 更多