ما هو "التخزين المحلي للخيط" في بايثون ولماذا أحتاجه؟

StackOverflow https://stackoverflow.com/questions/104983

سؤال

في لغة بايثون على وجه التحديد، كيف تتم مشاركة المتغيرات بين سلاسل الرسائل؟

على الرغم من أنني استخدمت threading.Thread قبل أن لم أفهم حقًا أو رأيت أمثلة لكيفية مشاركة المتغيرات.هل هي مشتركة بين الخيط الرئيسي والأطفال أم بين الأطفال فقط؟متى سأحتاج إلى استخدام التخزين المحلي لسلسلة المحادثات لتجنب هذه المشاركة؟

لقد رأيت العديد من التحذيرات حول مزامنة الوصول إلى البيانات المشتركة بين سلاسل الرسائل باستخدام الأقفال ولكني لم أر حتى الآن مثالاً جيدًا للمشكلة.

شكرا لك مقدما!

هل كانت مفيدة؟

المحلول

في بايثون، تتم مشاركة كل شيء، باستثناء المتغيرات الوظيفية المحلية (لأن كل استدعاء دالة يحصل على مجموعته الخاصة من المتغيرات المحلية، وتكون الخيوط دائمًا استدعاءات دالة منفصلة.) وحتى ذلك الحين، فقط المتغيرات نفسها (الأسماء التي تشير إلى الكائنات) محلية للوظيفة؛فالأشياء في حد ذاتها دائمًا ما تكون عالمية، وأي شيء يمكن أن يشير إليها.ال Thread كائن لمؤشر ترابط معين ليس كائنًا خاصًا في هذا الصدد.إذا قمت بتخزين Thread كائن في مكان ما يمكن لجميع سلاسل الرسائل الوصول إليه (مثل متغير عام) ثم يمكن لجميع سلاسل الرسائل الوصول إلى ذلك المتغير Thread هدف.إذا كنت تريد تعديل ذريًا أي شئ أنك لم تقم بإنشائه في هذا الموضوع نفسه، ولم تقم بتخزينه في أي مكان يمكن أن يصل إليه مؤشر ترابط آخر، فيجب عليك حمايته بقفل.ويجب بالطبع أن تشترك جميع الخيوط في نفس القفل، وإلا فلن يكون فعالًا جدًا.

إذا كنت تريد تخزينًا محليًا لمؤشر الترابط الفعلي، فهذا هو المكان threading.local ادخل.سمات threading.local لا يتم مشاركتها بين المواضيع.يرى كل مؤشر ترابط فقط السمات التي وضعها هناك.إذا كنت مهتمًا بتنفيذه، فالمصدر موجود _threading_local.py في المكتبة القياسية.

نصائح أخرى

خذ بعين الاعتبار الكود التالي:

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread, local

data = local()

def bar():
    print("I'm called from", data.v)

def foo():
    bar()

class T(Thread):
    def run(self):
        sleep(random())
        data.v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
 >> T().start(); T().start()
I'm called from Thread-2
I'm called from Thread-1 

هنا يتم استخدام threading.local() كطريقة سريعة وقذرة لتمرير بعض البيانات من run() إلى bar() دون تغيير واجهة foo().

لاحظ أن استخدام المتغيرات العامة لن يفي بالغرض:

#/usr/bin/env python

from time import sleep
from random import random
from threading import Thread

def bar():
    global v
    print("I'm called from", v)

def foo():
    bar()

class T(Thread):
    def run(self):
        global v
        sleep(random())
        v = self.getName()   # Thread-1 and Thread-2 accordingly
        sleep(1)
        foo()
 >> T().start(); T().start()
I'm called from Thread-2
I'm called from Thread-2 

وفي الوقت نفسه، إذا كان بإمكانك تمرير هذه البيانات كوسيطة للتابع foo() - فستكون طريقة أكثر أناقة وتصميمًا جيدًا:

from threading import Thread

def bar(v):
    print("I'm called from", v)

def foo(v):
    bar(v)

class T(Thread):
    def run(self):
        foo(self.getName())

ولكن هذا ليس ممكنًا دائمًا عند استخدام تعليمات برمجية تابعة لجهة خارجية أو سيئة التصميم.

يمكنك إنشاء تخزين محلي لمؤشر الترابط باستخدام threading.local().

>>> tls = threading.local()
>>> tls.x = 4 
>>> tls.x
4

ستكون البيانات المخزنة في TLS فريدة لكل موضوع مما سيساعد على ضمان عدم حدوث مشاركة غير مقصودة.

كما هو الحال في أي لغة أخرى، كل موضوع في بايثون لديه حق الوصول إلى نفس المتغيرات.لا يوجد فرق بين "الخيط الرئيسي" والخيوط الفرعية.

أحد الاختلافات مع Python هو أن Global Interpreter Lock يعني أن مؤشر ترابط واحد فقط يمكنه تشغيل كود Python في المرة الواحدة.هذا لا يساعد كثيرًا عندما يتعلق الأمر بمزامنة الوصول، حيث لا تزال جميع مشكلات الاستباقية المعتادة مطبقة، ويجب عليك استخدام أساسيات الترابط تمامًا كما هو الحال في اللغات الأخرى.هذا يعني أنك بحاجة إلى إعادة النظر فيما إذا كنت تستخدم مؤشرات الترابط للأداء.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top