2012-03-12 19 views
10

में बाल कार्यों से यूआई को कैसे अपडेट करें मुझे एक साधारण छोटा Winforms ऐप मिला है जो एक टीपीएल कार्य के माध्यम से किसी अन्य धागे पर लंबी चल रही प्रक्रिया करता है। इस लंबी चल रही प्रक्रिया के दौरान मैं यूआई (प्रगति पट्टी या कुछ) को अपडेट करना चाहता हूं। क्या ऐसा करने के बिना ऐसा करने का कोई तरीका है। ContinueWith()?WinForms

public partial class Form1 : Form 
{ 
    private Task _childTask; 

    public Form1() 
    { 
     InitializeComponent(); 

     Task.Factory.StartNew(() => 
     { 
      // Do some work 
      Thread.Sleep(1000); 

      // Update the UI 
      _childTask.Start(); 

      // Do more work 
      Thread.Sleep(1000); 
     }); 

     _childTask = new Task((antecedent) => 
     { 
      Thread.Sleep(2000); 
      textBox1.Text = "From child task"; 
     }, TaskScheduler.FromCurrentSynchronizationContext()); 


    } 
} 

इस कोड को मैं हर जगह अपवाद निष्पादित:

Cross-thread operation not valid: Control 'textBox1' accessed from a thread other than the thread it was created on.

+0

मुझे लगता है कि आपको थ्रेड पर टेक्स्टबॉक्स संदर्भ पेस्ट करना होगा। – jwillmer

उत्तर

10

हाँ, आप स्पष्ट रूप से BeginInvoke विंडो/नियंत्रण है कि आप के साथ बातचीत करना चाहते हैं पर कॉल कर सकते हैं। आपके मामले में यह इस तरह दिखेगा:

this.textBox.BeginInvoke(new Action(() => 
{ 
    this.textBox.Text = "From child task."; 
})); 
+0

यह निश्चित रूप से रास्ते पर दिखता है लेकिन मुझे संकलन त्रुटि मिलती है: 'सिस्टम। डिलीगेट' टाइप करने के लिए लैम्ब्डा अभिव्यक्ति को परिवर्तित नहीं किया जा सकता क्योंकि यह एक प्रतिनिधि प्रकार नहीं है – devlife

+0

मैंने एक क्रिया बनाई और अज्ञात विधि के बजाय कार्रवाई में पारित किया। निश्चित नहीं है कि उपर्युक्त क्यों काम नहीं करता है लेकिन मुझे वहां 99% रास्ता मिला है। धन्यवाद ड्रू। – devlife

+1

@devlife ओह, यह मेरी गलती है कि नए एपीआई के साथ काम करने के लिए इतना इस्तेमाल किया जा रहा है। हां, आपको पुराने WinForms BeginInvoke API के हस्ताक्षर के तरीके के कारण स्पष्ट रूप से एक नई क्रिया (...) के साथ लपेटने की आवश्यकता है। मैं अपना जवाब अपडेट करूंगा। –

5

आप TaskScheduler रूप state गुजर रहे हैं (या antecedent, जैसा कि आप यह कहा जाता है)। यह ज्यादा समझ में नहीं आता है।

मुझे यकीन नहीं है कि आप वास्तव में क्या करना चाहते हैं, लेकिन जब आप इसे बना रहे हैं तो Task शुरू करने पर आपको TaskScheduler निर्दिष्ट करने की आवश्यकता है। इसके अलावा, ऐसा लगता है कि childTask एक क्षेत्र होने के लिए नहीं है:

public Form1() 
{ 
    InitializeComponent(); 

    StartAsync(); 
} 

private async void StartAsync() 
{ 
    // do some work 
    await Task.Run(() => { Thread.Sleep(1000); }); 

    // start more work 
    var moreWork = Task.Run(() => { Thread.Sleep(1000); }); 

    // update the UI, based on data from “some work” 
    textBox1.Text = "From async method"; 

    // wait until “more work” finishes 
    await moreWork; 
} 

आप नहीं कर सकते हैं:, यह सब के साथ सी # 5 और await बहुत आसान होने जा रहा है

var scheduler = TaskScheduler.FromCurrentSynchronizationContext(); 

Task childTask = null; 

Task.Factory.StartNew(
    () => 
    { 
     Thread.Sleep(1000); 
     childTask.Start(scheduler); 
     Thread.Sleep(1000); 
    }); 

childTask = new Task(
    () => 
    { 
     Thread.Sleep(2000); 
     textBox1.Text = "From child task"; 
    }); 
बेशक

async कन्स्ट्रक्टर बनाएं, लेकिन आप इससे async विधि चला सकते हैं। "काम करना" यूआई थ्रेड पर नहीं चलता है, क्योंकि यह स्पष्ट रूप से Task.Run() के माध्यम से शुरू किया गया था। लेकिन चूंकि StartAsync() सीधे कॉल किया गया था, यह यूआई थ्रेड पर निष्पादित करता है।

+0

यह सिर्फ बात है। मैं तब तक इंतजार नहीं करना चाहता जब तक कि एक कार्य निष्पादित करने के लिए एक कार्य पूरा नहीं हो जाता। – devlife

+0

मैंने बेहतर प्रदर्शन करने के लिए अपना उदाहरण अपडेट किया जो मैं पार करने की कोशिश कर रहा था। – devlife

+0

उस स्थिति में, मेरा पहला नमूना अभी भी आपके लिए काम करेगा। मैंने सी # 5 संस्करण भी अपडेट किया। – svick