2011-07-01 15 views
6

नीचे दिए गए कोड में मैं कार्यों की सूची के परिणामों की रिपोर्टिंग सिंक्रनाइज़ करना चाहता हूं। यह अब काम कर रहा है क्योंकि कार्य। कार्य पूरा होने तक परिणाम ब्लॉक। हालांकि, कार्य आईडी = 3 को पूरा करने के लिए लंबा समय लगता है और अन्य सभी समाप्त कार्यों को उनकी स्थिति की रिपोर्ट करने से रोकता है।यूआई थ्रेड के बिना कार्य सिंक्रनाइज़ेशन

मुझे लगता है कि मैं रिपोर्टिंग (कंसोल। राइट) को एक में ले जाकर ऐसा कर सकता हूं। निर्देश के साथ, लेकिन मेरे पास यूआई थ्रेड नहीं है, तो मैं कार्य को सिंक्रनाइज़ करने के लिए टास्कशेड्यूलर कैसे प्राप्त करूं? कार्यों के साथ?

क्या मैं अब है:

static void Main(string[] args) 
{ 
    Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId); 

    var tasks = new List<Task<int>>(); 

    for (var i = 0; i < 10; i++) 
    { 
     var num = i; 
     var t = Task<int>.Factory.StartNew(() => 
     { 
      if (num == 3) 
      { 
       Thread.Sleep(20000); 
      } 
      Thread.Sleep(new Random(num).Next(1000, 5000)); 
      Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId); 
      return num; 
     }); 
     tasks.Add(t); 
    } 

    foreach (var task in tasks) 
    { 
     Console.WriteLine("Completed {0} on {1}", task.Result, Thread.CurrentThread.ManagedThreadId); 
    } 

    Console.WriteLine("End of Main"); 
    Console.ReadKey(); 
} 

मैं इस या कुछ इसी तरह करने के लिए स्थानांतरित करना चाहते हैं, लेकिन मैं Console.Write ("पूर्ण ...") सभी के लिए एक ही धागे पर होने की जरूरत है:

static void Main(string[] args) 
{ 
    Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId); 

    for (var i = 0; i < 10; i++) 
    { 
     var num = i; 
     Task<int>.Factory.StartNew(() => 
     { 
      if (num == 3) 
      { 
       Thread.Sleep(20000); 
      } 
      Thread.Sleep(new Random(num).Next(1000, 10000)); 
      Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId); 
      return num; 
     }).ContinueWith(value => 
     { 
      Console.WriteLine("Completed {0} on {1}", value.Result, Thread.CurrentThread.ManagedThreadId); 
     } 

    /* need syncronization context */); 
    } 

    Console.WriteLine("End of Main"); 
    Console.ReadKey(); 
} 

- समाधान - कुछ टिप्पणियां हो रही है और समाधान के कुछ पढ़ने के लिए इस पूर्ण समाधान है करता है कि मैं क्या चाहते हैं के बाद। लक्ष्य यहां जितना तेज़ हो सके लंबे समय तक चलने वाले कार्यों को संसाधित करना है और फिर एक समय में प्रत्येक कार्य के परिणामों के साथ कुछ करें।

static void Main(string[] args) 
{ 
    Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId); 

    var results = new BlockingCollection<int>(); 

    Task.Factory.StartNew(() => 
    { 
     while (!results.IsCompleted) 
     { 
      try 
      { 
       var x = results.Take(); 
       Console.WriteLine("Completed {0} on {1}", x, Thread.CurrentThread.ManagedThreadId); 
      } 
      catch (InvalidOperationException) 
      { 
      } 
     } 
     Console.WriteLine("\r\nNo more items to take."); 
    }); 

    var tasks = new List<Task>(); 

    for (var i = 0; i < 10; i++) 
    { 
     var num = i; 
     var t = Task.Factory.StartNew(() => 
     { 
      if (num == 3) 
      { 
       Thread.Sleep(20000); 
      } 
      Thread.Sleep(new Random(num).Next(1000, 10000)); 
      Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId); 
      results.Add(num); 
     }); 

     tasks.Add(t); 
    } 

    Task.Factory.ContinueWhenAll(tasks.ToArray(), _ => results.CompleteAdding()); 

    Console.WriteLine("End of Main"); 
    Console.ReadKey(); 
} 
+0

मुझे लगता है कंसोल धागा एक खड़े में एक जीयूआई के लिए है (WinForms/WPF)। जो एक अच्छा विचार नहीं है, एक डिस्पैचर/Messageloop की उपस्थिति एक (बड़ा) अंतर बनाता है। –

+1

अन्यथा, "उसी धागे पर होने" के साथ आपका क्या मतलब है इसके बारे में सोचें। तब तक ऐसा नहीं कर सकता जब तक वह धागा मतदान न हो। –

+0

मुझे इसे बदलने की ज़रूरत है ताकि एक ही समय में जारी रखने वाले कार्यों में से एक ही चल सके। इससे कोई फर्क नहीं पड़ता कि वे एक ही धागे पर चलते हैं या नहीं, लेकिन मैं उनमें से दो समानांतर में नहीं चल सकता। वास्तविक जीवन में मैं जारी रखता हूं भाग के साथ डेटाबेस में बहुत सारे डेटा लिख ​​रहे हैं। –

उत्तर

1

हालांकि, आप मन भी इस कार्य एक और देशी या प्रबंधित धागा पर फिर से निर्धारित किया जा सकता है में रखना किसी प्रकार की एक लेखक कार्य बनाना होगा! टीपीएल में डिफ़ॉल्ट शेड्यूलर का उपयोग करना आपके पास कोई नियंत्रण नहीं है जिस पर प्रबंधित थ्रेड काम प्राप्त करता है। यहां तक ​​कि अपने कोड ConcurrentConsole.WriteLine को भेजने () => String.Format("Completed {0} on {1}"..., ManagedThreadId सुनिश्चित ConcurrentConsole टास्क पर उठाया जा जाएगा साथ

End of Main 
Done 1 on 6 
Completed 1 on 6 
Done 5 on 9 
Completed 5 on 9 
Done 0 on 4 
Completed 0 on 4 
Done 2 on 5 
Completed 2 on 13 
Done 7 on 10 
Completed 7 on 10 
Done 4 on 8 
Completed 4 on 5 
Done 9 on 12 
Completed 9 on 9 
Done 6 on 6 
Completed 6 on 5 
Done 8 on 11 
Completed 8 on 4 
Done 3 on 7 
Completed 3 on 7 

, यह:

public class ConcurrentConsole 
{ 
    private static BlockingCollection<string> output 
     = new BlockingCollection<string>(); 

    public static Task CreateWriterTask(CancellationToken token) 
    { 
     return new Task(
      () => 
      { 
       while (!token.IsCancellationRequested) 
       { 
        string nextLine = output.Take(token); 
        Console.WriteLine(nextLine); 
       } 
      }, 
      token); 
    } 

    public static void WriteLine(Func<string> writeLine) 
    { 
     output.Add(writeLine()); 
    } 
} 

जब मैं इस का उपयोग करने के लिए अपने कोड बदल दिया मैं निम्नलिखित उत्पादन प्राप्त अभी भी उस धागे को बदल देगा जो यह चल रहा था। हालांकि निष्पादन कार्यों की तुलना में कम परिवर्तनशीलता के साथ।

+0

यह वास्तव में मेरे foreach कार्य पाश से भी बदतर है। यह किसी भी के परिणामों की रिपोर्ट करने से पहले सभी कार्यों को पूरा करने की प्रतीक्षा करता है। जो कुछ मैं ढूंढ रहा हूं वह कार्य पूरा होने की रिपोर्ट करने के लिए एक तरीका है जैसे ही यह किया जाता है और उस रिपोर्ट को एक थ्रेड में सिंक्रनाइज़ किया गया है। मैं दूसरों को रिपोर्टिंग से अवरुद्ध करने के लिए एक लंबा चलाना कार्य नहीं चाहता हूं। –

+0

@Ryan: मैंने एक विशिष्ट कार्य को रिपोर्टिंग जोड़ दी है, जो कि आप टीपीएल में सबसे अच्छी गारंटी दे सकते हैं। – user7116

+0

यह बिल्कुल ठीक नहीं है कि मैंने सिस्टम को कैसे कार्यान्वित किया लेकिन ब्लॉकिंग कोलेक्शन का उपयोग करने का विचार कार्यान्वयन के मूल में है। –

0

मेरा सुझाव है:

1) एक ताला वस्तु
2) स्ट्रिंग की एक सूची लिखे जाने की
3) एक धागा है कि लूप होता है, थोड़ा के लिए सो अंडे, तो ताला लगा बनाएं बनाना स्ट्रिंग्स की सूची, फिर यदि यह खाली नहीं है, तो उन सभी को लिखना और सूची खाली करना
4) अन्य थ्रेड फिर सूची को लॉक करते हैं, अपनी स्थिति जोड़ते हैं, अनलॉक करते हैं और जारी रखते हैं।

object writeListLocker = new object(); 
List<string> linesToWrite = new List<string>(); 

// Main thread loop 
for (; ;) 
{ 
    lock (writerListLocker) 
    { 
     foreach (string nextLine in linesToWrite) 
      Console.WriteLine(nextLine); 
     linesToWrite.Clear(); 
    } 
    Thread.Sleep(500); 
} 

// Reporting threads 
lock (writerListLocker) 
{ 
    linesToWrite.Add("Completed (etc.)"); 
} 
+0

यदि मैं टीपीएल का उपयोग जारी रखना चाहता हूं तो मैं कर सकते हैं। ऐसा लगता है कि उपर्युक्त धागे की श्रृंखला पर प्रसंस्करण की अनुमति देने जा रहा है, जबकि परिणामों का लेखन एक थ्रेड पर सिंक्रनाइज़ किया गया है। –

+0

लेकिन यह वही है जो यह करता है। मुख्य धागा पाश सभी लेखन करता है। अन्य थ्रेड सभी लिखने के बजाय लाइन ऑब्जेक्ट्स में जोड़ते हैं। –

1

आप OrderedTaskScheduler का उपयोग कर सकते हैं ताकि यह सुनिश्चित किया जा सके कि एक समय में केवल एक कार्य पूरा हो रहा है; हालांकि, वे थ्रेडपूल धागे पर चलेंगे (जरूरी नहीं कि सभी एक ही थ्रेड पर हों)।

यदि आपको वास्तव में एक ही थ्रेड (केवल एक समय में नहीं) पर सभी की आवश्यकता है, तो आप Nito.Async library से ActionThread का उपयोग कर सकते हैं। यह अपने कोड के लिए SynchronizationContext प्रदान करता है, जिसे FromCurrentSynchronizationContext द्वारा उठाया जा सकता है।

+0

यह वह दिशा है जिसे मैं जाना चाहता हूं। मैं यह सुनिश्चित करने के लिए कि उनमें से केवल एक ही समय में चलता है, जारी रखने के लिए एक ही धागे पर जारी रखें। निश्चित नहीं है कि अगर मैं उत्पादन में ऑर्डर्ड टास्कशेड्यूलर का उपयोग कर सकता हूं। मैं FromCurrentSynchronizationContext का उपयोग करना चाहता हूं लेकिन मैं इसलिए नहीं कर सकता क्योंकि मैं एक सेवा वातावरण में हूं (a.k.a. no UI thread) और SyncronizationContext.Current शून्य लौट रहा है। –

+0

मैं उत्पादन की गुणवत्ता के लिए 'ऑर्डर्ड टास्कशेड्यूलर' और 'एक्शन थ्रेड' दोनों पर विचार करता हूं। ऐसा लगता है कि उनमें से कोई भी आपकी समस्या का समाधान कर सकता है। –

0

मुझे लगता है कि आप निम्न के परिणाम की अपेक्षा करते हैं।

Starting on 8 
Done 1 on 11 
Completed 1 on 9 
Done 5 on 11 
Completed 5 on 9 
Done 0 on 10 
Completed 0 on 9 
Done 2 on 12 
Completed 2 on 9 
Done 7 on 16 
Completed 7 on 9 
Done 4 on 14 
Completed 4 on 9 
Done 9 on 18 
Completed 9 on 9 
Done 6 on 15 
Completed 6 on 9 
Done 8 on 17 
Completed 8 on 9 
Done 3 on 13 
Completed 3 on 9 

के रूप में नीचे, मैं StaSynchronizationContext मेरी कोड में the Understanding SynchronizationContext से जहां एक सूत्र में एक तुल्यकालन कॉल अच्छी तरह से समझाया गया है इस्तेमाल किया। कृपया, इसका संदर्भ लें।

मेरे कोड का टुकड़ा है:

static void Main(string[] args) 
{ 
    StaSynchronizationContext context = new StaSynchronizationContext(); 
    StaSynchronizationContext.SetSynchronizationContext(context); 
    Console.WriteLine("Starting on {0}", Thread.CurrentThread.ManagedThreadId); 
    for (var i = 0; i < 10; i++) 
    { 
     var num = i; 
     Task<int>.Factory.StartNew(() => 
     { 
      if (num == 3) 
      { 
       Thread.Sleep(20000); 
      } 
      Thread.Sleep(new Random(num).Next(1000, 10000)); 
      Console.WriteLine("Done {0} on {1}", num, Thread.CurrentThread.ManagedThreadId); 
      return num; 
     }).ContinueWith(
     value => 
     { 
      Console.WriteLine("Completed {0} on {1}", value.Result, Thread.CurrentThread.ManagedThreadId); 
     } 
     ,TaskScheduler.FromCurrentSynchronizationContext()); 
    } 
    Console.WriteLine("End of Main"); 
    Console.ReadKey(); 
} 
संबंधित मुद्दे