यह एमएसडीएन आलेख बहु-थ्रेड अनुप्रयोग विकास का पहला कदम है: संक्षेप में, इसका अर्थ है "ताले के साथ अपने साझा चर को सुरक्षित रखें (उर्फ महत्वपूर्ण वर्ग), क्योंकि आप सुनिश्चित नहीं हैं कि आपके द्वारा पढ़े गए/लिखने वाले डेटा सभी धागे के लिए वही "।
सीपीयू प्रति-कोर कैश संभावित मुद्दों में से एक है, जो गलत मान पढ़ने में अग्रणी होगा।रेस हालत में नेतृत्व करने वाला एक और मुद्दा एक ही समय में एक संसाधन को लिखने के लिए दो धागे हैं: यह जानना असंभव है कि बाद में कौन सा मूल्य संग्रहीत किया जाएगा।
चूंकि कोड डेटा को सुसंगत होने की अपेक्षा करता है, कुछ बहु-थ्रेड प्रोग्राम गलत तरीके से व्यवहार कर सकते हैं। बहु-थ्रेडिंग के साथ, आप सुनिश्चित नहीं हैं कि व्यक्तिगत निर्देशों के माध्यम से आपके द्वारा लिखे गए कोड को अपेक्षित रूप से निष्पादित किया जाता है, जब यह साझा चर के साथ संबंधित होता है।
InterlockedExchange/InterlockedIncrement
काम करता है, एक ताला उपसर्ग के साथ निम्न स्तर के एएसएम opcodes (या XCHG EDX,[EAX]
opcode की तरह, डिजाइन द्वारा बंद कर दिया) कर रहे हैं जो वास्तव में सभी सीपीयू कोर के लिए कैश जुटना के लिए बाध्य करेगा, और इसलिए एएसएम opcode निष्पादन धागा सुरक्षित बनाने के ।
उदाहरण के लिए, यहाँ कैसे एक स्ट्रिंग संदर्भ गिनती कार्यान्वित किया जाता है जब आप एक स्ट्रिंग मान निर्दिष्ट है (System.pas में _LStrAsg
देखते हैं - यह our optimized version of the RTL for Delphi 7/2002 से है - के बाद से डेल्फी मूल कोड कॉपीराइट):
MOV ECX,[EDX-skew].StrRec.refCnt
INC ECX { thread-unsafe increment ECX = reference count }
JG @@1 { ECX=-1 -> literal string -> jump not taken }
.....
@@1: LOCK INC [EDX-skew].StrRec.refCnt { ATOMIC increment of reference count }
MOV ECX,[EAX]
...
पहले INC ECX
और LOCK INC [EDX-skew].StrRec.refCnt
के बीच एक अंतर है - न केवल पहली वृद्धि ईसीएक्स और संदर्भ गणना चर नहीं है, लेकिन पहला थ्रेड-सुरक्षित नहीं है, जबकि दूसरा लॉक द्वारा प्रीफ़िक्स किया गया है इसलिए थ्रेड-सुरक्षित होगा।
वैसे, यह LOCK उपसर्ग multi-thread scaling in the RTL की समस्या में से एक है - यह नए CPUs के साथ बेहतर है, लेकिन फिर भी सही नहीं है। ,
var GlobalVariable: string;
GlobalSection: TRTLCriticalSection;
procedure TThreadOne.Execute;
var LocalVariable: string;
begin
...
EnterCriticalSection(GlobalSection);
LocalVariable := GlobalVariable+'a'; { modify GlobalVariable }
GlobalVariable := LocalVariable;
LeaveCriticalSection(GlobalSection);
....
end;
procedure TThreadTwp.Execute;
var LocalVariable: string;
begin
...
EnterCriticalSection(GlobalSection);
LocalVariable := GlobalVariable; { thread-safe read GlobalVariable }
LeaveCriticalSection(GlobalSection);
....
end;
एक स्थानीय चर का उपयोग करना महत्वपूर्ण अनुभाग कम करता है इसलिए अपने आवेदन ठीक से आंकने और पूरी शक्ति का उपयोग करेगा:
तो महत्वपूर्ण वर्गों का उपयोग कर एक कोड धागा सुरक्षित बनाने का सबसे आसान तरीका है आपके सीपीयू कोर का। EnterCriticalSection
और LeaveCriticalSection
के बीच, केवल एक धागा चल रहा होगा: अन्य धागे EnterCriticalSection
कॉल में प्रतीक्षा करेंगे ... तो महत्वपूर्ण अनुभाग जितना छोटा होगा, उतना तेज़ आपका आवेदन होगा। कुछ गलत तरीके से डिज़ाइन किए गए बहु-थ्रेडेड अनुप्रयोग वास्तव में मोनो-थ्रेडेड ऐप्स से धीमे हो सकते हैं!
और यह न भूलें कि महत्वपूर्ण अनुभाग के अंदर आपका कोड अपवाद बढ़ा सकता है, तो आपको लॉक रिलीज की सुरक्षा के लिए हमेशा एक स्पष्ट try ... finally LeaveCriticalSection() end;
ब्लॉक लिखना चाहिए, और अपने एप्लिकेशन के किसी भी मृत लॉक को रोकना चाहिए।
डेल्फी पूरी तरह से थ्रेड-सुरक्षित है यदि आप लॉक के साथ अपने साझा डेटा की रक्षा करते हैं, यानी एक गंभीर अनुभाग। सावधान रहें कि यहां तक कि संदर्भ-गिनती चर (जैसे स्ट्रिंग्स) को संरक्षित किया जाना चाहिए, भले ही उनके आरटीएल कार्यों के अंदर एक लॉक हो: यह LOCK सही संदर्भ गिनती मानने और स्मृति रिसाव से बचने के लिए है, लेकिन यह थ्रेड-सुरक्षित नहीं होगा । जितनी जल्दी हो सके इसे बनाने के लिए, see this SO question।
InterlockExchange
और InterlockCompareExchange
का उद्देश्य साझा सूचक परिवर्तक मान को बदलना है। आप पॉइंटर वैल्यू तक पहुंचने के लिए इसे महत्वपूर्ण अनुभाग के "हल्के" संस्करण के रूप में देख सकते हैं।
सभी मामलों में, काम कर रहे बहु थ्रेडेड कोड लिखना आसान नहीं है - यह हार्ड, as a Delphi expert just wrote in his blog भी है।
आपको या तो साझा किए गए डेटा के साथ सरल धागे लिखना चाहिए (थ्रेड शुरू होने से पहले डेटा की एक निजी प्रतिलिपि बनाएं, या केवल पढ़ने के लिए साझा डेटा का उपयोग करें - जो सार द्वारा थ्रेड-सुरक्षित है), या कुछ अच्छी तरह से कॉल करें डिजाइन और साबित पुस्तकालय - जैसे http://otl.17slon.com - जो आपको बहुत सारे डिबगिंग समय बचाएगा।
डेविड, अक्सर बूलियन, पूर्णांक इत्यादि परमाणु होने पर संदर्भित होते हैं (यदि सही ढंग से गठबंधन किया जाता है) और इस प्रकार थ्रेड सुरक्षित होता है। मुझे लगता है कि यहां स्वीकृत उत्तर http://stackoverflow.com/questions/510031/list-of-delphi-data-types-with-atomic-read-write-operations इसे परिप्रेक्ष्य में रखता है। उद्धरण "पढ़े धागे सुरक्षित हैं। लेखन धागे सुरक्षित नहीं हैं।" –
@LU RD आपको सटीक होने की आवश्यकता है। थ्रेडसेफ का क्या मतलब है? और ठीक है "रीड थ्रेड सुरक्षित हैं, लिखते हैं धागे सुरक्षित नहीं हैं" मतलब क्या है? –
यह केवल एक सामान्य चेतावनी थी कि यह मानने के लिए कि किसी भी थ्रेडिंग संदर्भों में उपयोग करने के लिए सुरक्षित तथाकथित परमाणु चर पर संचालन पर विचार नहीं कर सकता है। –