मैं इसके लिए TPL डेटाफ़्लो का उपयोग करूँगा (क्योंकि आप .NET 4.5 का उपयोग कर रहे हैं और यह Task
आंतरिक रूप से उपयोग करता है )। ActionBlock<TInput>
यह संसाधित होने के बाद आप आसानी से एक पोस्ट पोस्ट आइटम बना सकते हैं और यह उचित मात्रा में इंतजार कर रहा है।
सबसे पहले, एक कारखाना बनाएँ जो आपके कभी न खत्म होने वाले कार्य का निर्माण करेगा:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Action<DateTimeOffset> action, CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action.
action(now);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
मैंने ActionBlock<TInput>
एक DateTimeOffset
संरचना लेने के लिए चुना है ; आपको एक प्रकार का पैरामीटर पास करना होगा, और यह कुछ उपयोगी स्थिति को पारित कर सकता है (यदि आप चाहें तो राज्य की प्रकृति को बदल सकते हैं)।
यह भी ध्यान दें कि ActionBlock<TInput>
डिफ़ॉल्ट रूप से एक समय में केवल एक आइटम संसाधित करता है, इसलिए आपको गारंटी दी जाती है कि केवल एक ही कार्रवाई संसाधित की जाएगी (इसका अर्थ है, आपको विस्तार विधि को कॉल करने पर पुनर्व्यवस्थितता से नहीं निपटना होगाPost
को स्वयं वापस )।
मैंने CancellationToken
संरचना के दोनों ActionBlock<TInput>
और Task.Delay
विधि कॉल के लिए संरचना को पारित कर दिया है ; यदि प्रक्रिया रद्द हो जाती है, तो पहले संभावित अवसर पर रद्द कर दिया जाएगा।
वहां से, आपके द्वारा कार्यान्वित ITargetBlock<DateTimeoffset>
इंटरफ़ेस को स्टोर करने के लिए यह आपके कोड का एक आसान रीफ़ैक्टरिंग है ActionBlock<TInput>
(यह उच्च-स्तरीय एब्सट्रैक्ट है जो उपभोक्ता हैं ब्लॉकों का प्रतिनिधित्व करते हैं, और आप कॉल को Post
एक्सटेंशन विधि के माध्यम से उपभोग को ट्रिगर करना चाहते हैं ):
CancellationTokenSource wtoken;
ActionBlock<DateTimeOffset> task;
आपका StartWork
तरीका:
void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();
// Set the task.
task = CreateNeverEndingTask(now => DoWork(), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now);
}
और फिर आपकी StopWork
विधि:
void StopWork()
{
// CancellationTokenSource implements IDisposable.
using (wtoken)
{
// Cancel. This will cancel the task.
wtoken.Cancel();
}
// Set everything to null, since the references
// are on the class level and keeping them around
// is holding onto invalid state.
wtoken = null;
task = null;
}
आप यहाँ TPL Dataflow का उपयोग क्यों करना चाहेंगे? कुछ कारण:
चिंताओ का विभाजन
CreateNeverEndingTask
विधि अब एक कारखाने हैं जो आपके "सेवा" इतनी बात करने के लिए बनाता है। जब आप शुरू करते हैं और रुक जाते हैं, तो आप इसे नियंत्रित करते हैं और यह पूरी तरह से आत्म-निहित है। आपको अपने कोड के अन्य पहलुओं के साथ टाइमर के राज्य नियंत्रण को इंटरवाइव करने की आवश्यकता नहीं है। आप बस ब्लॉक बनाते हैं, इसे शुरू करते हैं, और जब आप पूरा कर लेते हैं तो इसे रोक देते हैं।
धागे / कार्यों / संसाधनों का अधिक कुशल उपयोग
TPL डेटा प्रवाह में ब्लॉक के लिए डिफ़ॉल्ट अनुसूचक एक के लिए समान है Task
, जो थ्रेड पूल है। ActionBlock<TInput>
अपनी कार्रवाई को संसाधित करने के लिए और साथ ही कॉल टू Task.Delay
का उपयोग करके, आप उस थ्रेड का नियंत्रण नियंत्रित कर रहे हैं जिसका उपयोग आप तब कर रहे थे जब आप वास्तव में कुछ भी नहीं कर रहे थे। दी, यह वास्तव में कुछ ओवरहेड की ओर जाता है जब आप नए को स्पॉन करते हैं Task
जो निरंतरता को संसाधित करेगा, लेकिन यह छोटा होना चाहिए, यह देखते हुए कि आप इसे एक तंग लूप में संसाधित नहीं कर रहे हैं (आप इनवोक के बीच दस सेकंड इंतजार कर रहे हैं)।
यदि DoWork
फ़ंक्शन वास्तव में प्रतीक्षा योग्य बनाया जा सकता है (अर्थात्, इसमें रिटर्न होता है Task
), तो आप (संभवतः) एक के Func<DateTimeOffset, CancellationToken, Task>
बजाय लेने के लिए ऊपर फैक्ट्री विधि को ट्वीक करके इसे और भी अधिक अनुकूलित कर सकते हैं Action<DateTimeOffset>
, जैसे:
ITargetBlock<DateTimeOffset> CreateNeverEndingTask(
Func<DateTimeOffset, CancellationToken, Task> action,
CancellationToken cancellationToken)
{
// Validate parameters.
if (action == null) throw new ArgumentNullException("action");
// Declare the block variable, it needs to be captured.
ActionBlock<DateTimeOffset> block = null;
// Create the block, it will call itself, so
// you need to separate the declaration and
// the assignment.
// Async so you can wait easily when the
// delay comes.
block = new ActionBlock<DateTimeOffset>(async now => {
// Perform the action. Wait on the result.
await action(now, cancellationToken).
// Doing this here because synchronization context more than
// likely *doesn't* need to be captured for the continuation
// here. As a matter of fact, that would be downright
// dangerous.
ConfigureAwait(false);
// Wait.
await Task.Delay(TimeSpan.FromSeconds(10), cancellationToken).
// Same as above.
ConfigureAwait(false);
// Post the action back to the block.
block.Post(DateTimeOffset.Now);
}, new ExecutionDataflowBlockOptions {
CancellationToken = cancellationToken
});
// Return the block.
return block;
}
बेशक, CancellationToken
आपकी विधि के माध्यम से बुनाई करना अच्छा होगा (यदि यह एक को स्वीकार करता है), जो यहां किया जाता है।
इसका मतलब है कि आपके पास DoWorkAsync
निम्नलिखित हस्ताक्षर के साथ एक विधि होगी :
Task DoWorkAsync(CancellationToken cancellationToken);
आपको बदलना होगा (केवल थोड़ा सा, और आप यहां से चिंताओं को अलग नहीं कर रहे हैं) StartWork
विधि को पारित करने के लिए नए हस्ताक्षर करने की CreateNeverEndingTask
विधि, जैसे कि:
void StartWork()
{
// Create the token source.
wtoken = new CancellationTokenSource();
// Set the task.
task = CreateNeverEndingTask((now, ct) => DoWorkAsync(ct), wtoken.Token);
// Start the task. Post the time.
task.Post(DateTimeOffset.Now, wtoken.Token);
}