यहां एक दिलचस्प लाइब्रेरी लेखक की दुविधा है। मेरी लाइब्रेरी में (मेरे मामले में EasyNetQ) मैं थ्रेड स्थानीय संसाधनों को आवंटित कर रहा हूं। तो जब कोई ग्राहक एक नया धागा बनाता है और फिर मेरी लाइब्रेरी पर कुछ तरीकों को कॉल करता है तो नए संसाधन बनाए जाते हैं। EasyNetQ के मामले में जब RabbitMQ सर्वर के लिए एक नया चैनल बनाया जाता है तो क्लाइंट नए थ्रेड पर 'प्रकाशित' कहता है। मैं यह पता लगाने में सक्षम होना चाहता हूं कि क्लाइंट थ्रेड निकलता है ताकि मैं संसाधन (चैनल) को साफ़ कर सकूं।जब कोई क्लाइंट थ्रेड निकलता है तो मैं कैसे पता लगा सकता हूं?
ऐसा करने का एकमात्र तरीका मैं एक नया 'वॉचर' थ्रेड बनाने के लिए आया हूं जो क्लाइंट थ्रेड में एक जॉइन कॉल पर बस ब्लॉक करता है। यहां एक साधारण प्रदर्शन:
पहले मेरी 'लाइब्रेरी'। यह ग्राहक धागा पकड़ लेता है और फिर एक नया धागा 'शामिल' पर जो ब्लॉक बनाता है:
public class Library
{
public void StartSomething()
{
Console.WriteLine("Library says: StartSomething called");
var clientThread = Thread.CurrentThread;
var exitMonitorThread = new Thread(() =>
{
clientThread.Join();
Console.WriteLine("Libaray says: Client thread existed");
});
exitMonitorThread.Start();
}
}
यहाँ एक ग्राहक मेरे पुस्तकालय का उपयोग करता है है। यह एक नया धागा बनाता है और फिर मेरी लाइब्रेरी की StartSomething विधि कॉल:
public class Client
{
private readonly Library library;
public Client(Library library)
{
this.library = library;
}
public void DoWorkInAThread()
{
var thread = new Thread(() =>
{
library.StartSomething();
Thread.Sleep(10);
Console.WriteLine("Client thread says: I'm done");
});
thread.Start();
}
}
जब मैं इस तरह ग्राहक चलाएँ:
Library says: StartSomething called
Client thread says: I'm done
Libaray says: Client thread existed
तो यह काम करता है:
var client = new Client(new Library());
client.DoWorkInAThread();
// give the client thread time to complete
Thread.Sleep(100);
मैं इस आउटपुट प्राप्त , लेकिन यह बदसूरत है। मुझे वास्तव में इन सभी अवरुद्ध वॉचर धागे के बारे में विचार पसंद नहीं है। क्या ऐसा करने का कोई बेहतर तरीका है?
पहला विकल्प।
एक ऐसी विधि प्रदान करें जो एक कार्यकर्ता लौटाती है जो आईडीस्पोज़ेबल लागू करती है और दस्तावेज़ीकरण में स्पष्ट करती है कि आपको थ्रेड्स के बीच श्रमिकों को साझा नहीं करना चाहिए।
public class Library
{
public LibraryWorker GetLibraryWorker()
{
return new LibraryWorker();
}
}
public class LibraryWorker : IDisposable
{
public void StartSomething()
{
Console.WriteLine("Library says: StartSomething called");
}
public void Dispose()
{
Console.WriteLine("Library says: I can clean up");
}
}
ग्राहक अब एक छोटे से अधिक जटिल है:: यहाँ संशोधित पुस्तकालय है
public class Client
{
private readonly Library library;
public Client(Library library)
{
this.library = library;
}
public void DoWorkInAThread()
{
var thread = new Thread(() =>
{
using(var worker = library.GetLibraryWorker())
{
worker.StartSomething();
Console.WriteLine("Client thread says: I'm done");
}
});
thread.Start();
}
}
इस परिवर्तन के साथ मुख्य समस्या यह है कि यह एपीआई के लिए एक को तोड़ने परिवर्तन है। मौजूदा ग्राहकों को फिर से लिखा जाना होगा। अब यह इतनी बुरी चीज नहीं है, इसका मतलब यह होगा कि उनका पुनरीक्षण करना और यह सुनिश्चित करना कि वे सही तरीके से सफाई कर रहे हैं।
गैर-ब्रेकिंग दूसरा विकल्प। एपीआई क्लाइंट के लिए 'कार्यक्षेत्र' घोषित करने का एक तरीका प्रदान करता है। एक बार दायरा पूरा हो जाने पर, लाइब्रेरी साफ हो सकती है। पुस्तकालय एक WorkScope कि IDisposable लागू करता है प्रदान करता है, लेकिन इसके बाद के संस्करण पहले विकल्प के विपरीत, StartSomething विधि पुस्तकालय वर्ग पर रहता है:
public class Library
{
public WorkScope GetWorkScope()
{
return new WorkScope();
}
public void StartSomething()
{
Console.WriteLine("Library says: StartSomething called");
}
}
public class WorkScope : IDisposable
{
public void Dispose()
{
Console.WriteLine("Library says: I can clean up");
}
}
ग्राहक बस एक WorkScope में StartSomething कॉल डालता है ...
public class Client
{
private readonly Library library;
public Client(Library library)
{
this.library = library;
}
public void DoWorkInAThread()
{
var thread = new Thread(() =>
{
using(library.GetWorkScope())
{
library.StartSomething();
Console.WriteLine("Client thread says: I'm done");
}
});
thread.Start();
}
}
मुझे यह पहले विकल्प से कम पसंद है क्योंकि यह पुस्तकालय उपयोगकर्ता को दायरे के बारे में सोचने के लिए मजबूर नहीं करता है।
'मैं थ्रेड स्थानीय संसाधनों को आवंटित कर रहा हूं' - यह अच्छी शुरुआत नहीं है :( –
दुर्भाग्यवश यह निम्न स्तर की एएमक्यूपी लाइब्रेरी की बाधा है, आपको थ्रेड के बीच चैनल साझा करने की अनुमति नहीं है। मुझे लगता है कि एक वैकल्पिक एपीआई डिज़ाइन लाइब्रेरी के लिए गैर-थ्रेड-सुरक्षित 'प्रकाशक' प्रदान करने के लिए होगा जिसे क्लाइंट को बनाना आवश्यक है। –
यह सुनिश्चित नहीं है कि मैं उपयोग केस और नमूना कोड को सही ढंग से सहसंबंधित करता हूं। क्या EasyNetQ के क्लाइंट कोड को एक नया प्रारंभ करने की आवश्यकता होगी लाइब्रेरी का उपयोग करने के लिए थ्रेड? क्या आप अधिक वास्तविक क्लाइंट कोड डाल सकते हैं? यदि नहीं, तो var ctx = library.StartExecutionContext(); ... ctx.Complete(); फिर लाइब्रेरी ctx.Complete के अंदर एक मैनुअल रीसेट इवेंट का उपयोग करके, सफाई होगी –