कॉलिंग थ्रेड इस ऑब्जेक्ट तक नहीं पहुँच सकता क्योंकि एक अलग थ्रेड इसका स्वामी है


341

मेरा कोड नीचे है

public CountryStandards()
{
    InitializeComponent();
    try
    {
        FillPageControls();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message, "Country Standards", MessageBoxButton.OK, MessageBoxImage.Error);
    }
}

/// <summary>
/// Fills the page controls.
/// </summary>
private void FillPageControls()
{
    popUpProgressBar.IsOpen = true;
    lblProgress.Content = "Loading. Please wait...";
    progress.IsIndeterminate = true;
    worker = new BackgroundWorker();
    worker.DoWork += new System.ComponentModel.DoWorkEventHandler(worker_DoWork);
    worker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(worker_ProgressChanged);
    worker.WorkerReportsProgress = true;
    worker.WorkerSupportsCancellation = true;
    worker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(worker_RunWorkerCompleted);
    worker.RunWorkerAsync();                    
}

private void worker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
    GetGridData(null, 0); // filling grid
}

private void worker_ProgressChanged(object sender, System.ComponentModel.ProgressChangedEventArgs e)
{
    progress.Value = e.ProgressPercentage;
}

private void worker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
    worker = null;
    popUpProgressBar.IsOpen = false;
    //filling Region dropdown
    Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards();
    objUDMCountryStandards.Operation = "SELECT_REGION";
    DataSet dsRegionStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
    if (!StandardsDefault.IsNullOrEmptyDataTable(dsRegionStandards, 0))
        StandardsDefault.FillComboBox(cmbRegion, dsRegionStandards.Tables[0], "Region", "RegionId");

    //filling Currency dropdown
    objUDMCountryStandards = new Standards.UDMCountryStandards();
    objUDMCountryStandards.Operation = "SELECT_CURRENCY";
    DataSet dsCurrencyStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
    if (!StandardsDefault.IsNullOrEmptyDataTable(dsCurrencyStandards, 0))
        StandardsDefault.FillComboBox(cmbCurrency, dsCurrencyStandards.Tables[0], "CurrencyName", "CurrencyId");

    if (Users.UserRole != "Admin")
        btnSave.IsEnabled = false;

}

/// <summary>
/// Gets the grid data.
/// </summary>
/// <param name="sender">The sender.</param>
/// <param name="pageIndex">Index of the page.( used in case of paging)   </pamam>
private void GetGridData(object sender, int pageIndex)
{
    Standards.UDMCountryStandards objUDMCountryStandards = new Standards.UDMCountryStandards();
    objUDMCountryStandards.Operation = "SELECT";
    objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;
    DataSet dsCountryStandards = objStandardsBusinessLayer.GetCountryStandards(objUDMCountryStandards);
    if (!StandardsDefault.IsNullOrEmptyDataTable(dsCountryStandards, 0) && (chkbxMarketsSearch.IsChecked == true || chkbxBudgetsSearch.IsChecked == true || chkbxProgramsSearch.IsChecked == true))
    {
        DataTable objDataTable = StandardsDefault.FilterDatatableForModules(dsCountryStandards.Tables[0], "Country", chkbxMarketsSearch, chkbxBudgetsSearch, chkbxProgramsSearch);
        dgCountryList.ItemsSource = objDataTable.DefaultView;
    }
    else
    {
        MessageBox.Show("No Records Found", "Country Standards", MessageBoxButton.OK, MessageBoxImage.Information);
        btnClear_Click(null, null);
    }
}

objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;ग्रिड डेटा प्राप्त करने का चरण अपवाद फेंकता है

कॉलिंग थ्रेड इस ऑब्जेक्ट तक नहीं पहुँच सकता क्योंकि एक अलग थ्रेड इसका स्वामी है।

यहाँ क्या गलत है?


जवाबों:


698

यह एक आम समस्या है जिससे लोग परेशान हो रहे हैं। जब भी आप अपने UI तत्वों को मुख्य थ्रेड के अलावा किसी थ्रेड से अपडेट करते हैं, तो आपको उपयोग करने की आवश्यकता होती है:

this.Dispatcher.Invoke(() =>
{
    ...// your code here.
});

आप यह control.Dispatcher.CheckAccess()जांचने के लिए भी उपयोग कर सकते हैं कि वर्तमान धागा नियंत्रण का मालिक है या नहीं। यदि यह स्वयं करता है, तो आपका कोड सामान्य दिखता है। अन्यथा, उपरोक्त पैटर्न का उपयोग करें।


3
मुझे ओपी जैसी ही समस्या है; मेरी समस्या अब यह है कि घटना अब एक ढेर अतिप्रवाह का कारण बनती है। : \
मालवोस २२'१४

2
अपने पुराने प्रोजेक्ट पर वापस गया और इसे हल किया। इसके अलावा, मैं इसे +1 करना भूल गया था। यह तरीका काफी अच्छा काम करता है! यह हमारे स्थानीय संसाधनों को लोड करने के लिए थ्रेड्स का उपयोग करके 10 सेकंड या उससे भी अधिक समय पर मेरे एप्लिकेशन लोडिंग समय को बेहतर बनाता है। चीयर्स!
मालवोस

4
अगर मैं गलत नहीं हूँ, तो आप गैर-स्वामी धागे से UI ऑब्जेक्ट नहीं पढ़ सकते हैं; मुझे थोड़ा आश्चर्य हुआ।
इलियट

32
Application.Current.Dispatcher.Invoke(MyMethod, DispatcherPriority.ContextIdle);डिस्पैचर प्राप्त करने के लिए यदि यूआई थ्रेड पर इस उत्तर के
जंपिंग जेज्जा

2
+1। हा! मैंने इसका इस्तेमाल कुछ WPF हैकरी के लिए चीजों को डिकॉउंड करने के लिए किया। मैं एक स्थिर संदर्भ में था इसलिए मैं उपयोग नहीं कर सका this.Dispatcher.Invoke.... इसके बजाय ... myControl.Dispatcher.Invoke:) मुझे एक वस्तु वापस करने की आवश्यकता थी इसलिए मैंने किया myControlDispatcher.Invoke<object>(() => myControl.DataContext);
सी। टिवाल्ट

52

Dispatcher.Invokeअन्य कार्यों को करने वाले फ़ंक्शन में UI को तुरंत अपडेट करने के लिए एक और अच्छा उपयोग है:

// Force WPF to render UI changes immediately with this magic line of code...
Dispatcher.Invoke(new Action(() => { }), DispatcherPriority.ContextIdle);

मैं बटन टेक्स्ट को " प्रोसेसिंग ... " अपडेट करने के लिए उपयोग करता हूं और WebClientअनुरोध करते समय इसे अक्षम करता हूं ।


4
इस जवाब पर मेटा पर चर्चा की जा रही है। meta.stackoverflow.com/questions/361844/…
JDB को अभी भी मोनिका

इसने इंटरनेट से डेटा प्राप्त करने से मेरा नियंत्रण रोक दिया?
वसीम अहमद नईम

41

मेरे 2 सेंट जोड़ने के लिए, अपवाद तब भी हो सकता है जब आप अपने कोड को कॉल करते हैं System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()
मुद्दा यह है कि आपको उस नियंत्रण का कॉल Invoke()करना होगा जिसे आप एक्सेस करने का प्रयास कर रहे हैं , जो कुछ मामलों में समान नहीं हो सकता है । इसलिए इसके बजाय आपको सुरक्षित रहने के लिए उपयोग करना चाहिए। इससे पहले कि मुझे यह एहसास होता मैं कुछ घंटों के लिए अपना सिर पीट रहा था।DispatcherSystem.Windows.Threading.Dispatcher.CurrentDispatcherYourControl.Dispatcher.Invoke()

अपडेट करें

भविष्य के पाठकों के लिए, ऐसा लगता है कि यह .NET (4.0 और ऊपर) के नए संस्करणों में बदल गया है। अब आपको अपने वीएम में यूआई-बैकिंग गुणों को अपडेट करते समय सही डिस्पैचर के बारे में चिंता करने की आवश्यकता नहीं है। WPF इंजन सही UI थ्रेड पर क्रॉस-थ्रेड कॉल को मार्शल करेगा। अधिक विवरण यहां देखें । जानकारी और लिंक के लिए @aronburro को धन्यवाद। आप नीचे दिए गए हमारे वार्तालाप को भी टिप्पणियों में पढ़ना चाह सकते हैं।


4
@ l33t: WPF एक आवेदन में कई UI थ्रेड्स का समर्थन करता है, जिनमें से प्रत्येक का अपना होगा Dispatcher। उन मामलों में (जो स्वाभाविक रूप से दुर्लभ हैं), कॉलिंग Control.Dispatcherसुरक्षित तरीका है। संदर्भ के लिए आप इस लेख के साथ ही इस एसओ पोस्ट (विशेष रूप से स्क्विडवर्ड का उत्तर) को देख सकते हैं ।
डॉटनेट

1
दिलचस्प बात यह है कि मैं इस अपवाद का सामना कर रहा था, जब मैंने गुगली की और इस पेज पर उतरा और जैसे हम में से अधिकांश ने सबसे ज्यादा वोट देने वाले उत्तर की कोशिश की, जिसने तब मेरा मुद्दा हल नहीं किया। मैंने तब इस कारण का पता लगा लिया और इसे यहां सहकर्मी डेवलपर्स के लिए पोस्ट कर दिया।
डॉटनेट

1
@ l33t, यदि आप MVVM का सही उपयोग कर रहे हैं, तो यह समस्या नहीं होनी चाहिए। दृश्य जरूरी जानता है कि डिस्पैचर का उपयोग क्या है, जबकि ViewModels और मॉडल नियंत्रण के कुछ भी नहीं जानते हैं और नियंत्रण को जानने की कोई आवश्यकता नहीं है।
अनारोनब्रो

1
@ARonburro: समस्या यह है कि VM वैकल्पिक थ्रेड्स (जैसे कार्य, टाइमर-आधारित कार्य, समानांतर क्वेरी) पर कार्रवाई शुरू करना चाहता है, और जैसे-जैसे ऑपरेशन आगे बढ़ता है, यूआई को अपडेट करना पसंद कर सकता है (RaisePropertyChanged आदि के माध्यम से), जो बदले में कोशिश करेगा गैर- UI थ्रेड से UI नियंत्रण तक पहुँचने और इस प्रकार इस अपवाद के परिणामस्वरूप। मुझे सही MVVM दृष्टिकोण का पता नहीं है जो इस समस्या को हल करेगा।
डॉटनेट

1
WPF बाइंडिंग इंजन स्वचालित रूप से घटनाओं को सही डिस्पैचर में बदल देता है। यही कारण है कि वीएम को डिस्पैचर के बारे में जानने की कोई आवश्यकता नहीं है; यह सब करना है बस संपत्ति बदल घटनाओं को बढ़ाने के लिए है। WinForms बाइंडिंग एक अलग कहानी है।
अनारोनब्रो

34

यदि आप इस समस्या का सामना करते हैं और WP नियंत्रण के साथ या WPF में काम करते समय एक अलग वर्कर थ्रेड पर UI नियंत्रण बनाए गए थे , तो किसी भी विधि के लिए या पैरामीटर के रूप में पास करने से पहले कॉल विधि। ऐसे उदाहरणों का उपयोग करना काम नहीं करता हैBitmapSourceImageSourceFreeze()BitmapSourceImageSourceApplication.Current.Dispatcher.Invoke()


24
आह, एक अच्छी पुरानी अस्पष्ट और रहस्यमय चाल की तरह कुछ भी नहीं कुछ भी हल करने के लिए कोई भी समझता है।
एडविन

2
मुझे इस बारे में अधिक जानकारी है कि यह क्यों काम करता है और मैं इसे कैसे समझ सकता हूं।
जेवियर शाइ


25

यह मेरे साथ हुआ क्योंकि मैंने इसमें access UIकंपोनेंट करने की कोशिश की थीanother thread insted of UI thread

इस तरह

private void button_Click(object sender, RoutedEventArgs e)
{
    new Thread(SyncProcces).Start();
}

private void SyncProcces()
{
    string val1 = null, val2 = null;
    //here is the problem 
    val1 = textBox1.Text;//access UI in another thread
    val2 = textBox2.Text;//access UI in another thread
    localStore = new LocalStore(val1);
    remoteStore = new RemoteStore(val2);
}

इस समस्या को हल करने के लिए, अपने उत्तर में ऊपर वर्णित कैंडिड के अंदर किसी भी यूआई कॉल को लपेटें

private void SyncProcces()
{
    string val1 = null, val2 = null;
    this.Dispatcher.Invoke((Action)(() =>
    {//this refer to form in WPF application 
        val1 = textBox.Text;
        val2 = textBox_Copy.Text;
    }));
    localStore = new LocalStore(val1);
    remoteStore = new RemoteStore(val2 );
}

1
Upvoted, क्योंकि यह वह जगह है नहीं एक नकली जवाब या plagiaristic, लेकिन यह बजाय कि अन्य उत्तर, कमी थी, जबकि क्या पहले पोस्ट किया गया था के लिए ऋण देने के एक अच्छा उदाहरण प्रदान करता है।
Panzercrisis

अपवोट स्पष्ट उत्तर के लिए है। हालाँकि ऐसा ही दूसरों के द्वारा लिखा गया था, लेकिन यह किसी के लिए भी स्पष्ट है जो अटका हुआ है।
निशांत मॉ

15

किसी कारण के लिए कैंडीड का जवाब नहीं बना। हालाँकि, यह मददगार था, क्योंकि इसने मुझे इसे खोजने के लिए प्रेरित किया, जिसने पूरी तरह से काम किया:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
{
   //your code here...
}));

यह संभव है कि आपने फॉर्म की कक्षा से कॉल नहीं किया हो। या तो आप विंडो के संदर्भ को ले सकते हैं, या आप संभवतः आपके द्वारा सुझाए गए उपयोग कर सकते हैं।
सिमोन

4
यदि यह आपके लिए काम करता है, तो पहली बार में इसका उपयोग करना अनावश्यक था। System.Windows.Threading.Dispatcher.CurrentDispatcherहै वर्तमान थ्रेड के लिए डिस्पैचर । मतलब यह है कि अगर आप एक पृष्ठभूमि धागा रहे हैं तो उसे है नहीं यूआई धागा के डिस्पैचर होने जा रहा। यूआई थ्रेड के डिस्पैचर तक पहुंचने के लिए, उपयोग करें System.Windows.Application.Current.Dispatcher

13

आपको यूआई में अपडेट करने की आवश्यकता है, इसलिए उपयोग करें

Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)})); 

4

यह मेरे लिए काम करता है।

new Thread(() =>
        {

        Thread.CurrentThread.IsBackground = false;
        Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, (SendOrPostCallback)delegate {

          //Your Code here.

        }, null);
        }).Start();

3

मैंने यह भी पाया कि System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()हमेशा लक्ष्य नियंत्रण के डिस्पैचर नहीं होते हैं, जैसा कि डॉटनेट ने अपने उत्तर में लिखा है। मेरे पास डिस्पैचर को नियंत्रित करने के लिए पहुंच नहीं थी, इसलिए मैंने इसका इस्तेमाल किया Application.Current.Dispatcherऔर इसने समस्या को हल किया।


2

समस्या यह है कि आप GetGridDataबैकग्राउंड थ्रेड से कॉल कर रहे हैं । यह विधि कई WPF नियंत्रणों तक पहुंचती है जो मुख्य धागे से बंधे होते हैं। पृष्ठभूमि थ्रेड से उन्हें एक्सेस करने का कोई भी प्रयास इस त्रुटि को जन्म देगा।

सही धागे को वापस पाने के लिए आपको उपयोग करना चाहिए SynchronizationContext.Current.Post। हालाँकि इस विशेष मामले में ऐसा लगता है कि आप जो काम कर रहे हैं वह अधिकांश यूआई आधारित है। इसलिए आप यूआई थ्रेड पर तुरंत वापस जाने और कुछ काम करने के लिए बैकग्राउंड थ्रेड बना रहे होंगे। आपको अपने कोड को थोड़ा रिफलेक्टर करने की आवश्यकता है ताकि यह बैकग्राउंड थ्रेड पर महंगा काम कर सके और फिर बाद में यूआई में नया डेटा पोस्ट कर सके।


2

जैसा कि यहां बताया गया है , Dispatcher.Invokeयूआई को फ्रीज कर सकता है। के Dispatcher.BeginInvokeबजाय का उपयोग करना चाहिए ।

चेकिंग और कॉलिंग डिस्पैचर आह्वान को सरल बनाने के लिए यहां एक आसान एक्सटेंशन क्लास है।

नमूना उपयोग: (WPF विंडो से कॉल करें)

this Dispatcher.InvokeIfRequired(new Action(() =>
{
    logTextbox.AppendText(message);
    logTextbox.ScrollToEnd();
}));

एक्सटेंशन क्लास:

using System;
using System.Windows.Threading;

namespace WpfUtility
{
    public static class DispatcherExtension
    {
        public static void InvokeIfRequired(this Dispatcher dispatcher, Action action)
        {
            if (dispatcher == null)
            {
                return;
            }
            if (!dispatcher.CheckAccess())
            {
                dispatcher.BeginInvoke(action, DispatcherPriority.ContextIdle);
                return;
            }
            action();
        }
    }
}

0

इसके अलावा, एक अन्य समाधान यह सुनिश्चित करना है कि आपके नियंत्रण UI थ्रेड में बनाए गए हैं, उदाहरण के लिए पृष्ठभूमि कार्यकर्ता थ्रेड द्वारा नहीं।


0

जब मैंने अपने WPF अनुप्रयोग में कैस्केडिंग कॉम्बोबॉक्स जोड़ दिया तो मुझे त्रुटि मिलती रही, और इस एपीआई का उपयोग करके त्रुटि को हल किया:

    using System.Windows.Data;

    private readonly object _lock = new object();
    private CustomObservableCollection<string> _myUiBoundProperty;
    public CustomObservableCollection<string> MyUiBoundProperty
    {
        get { return _myUiBoundProperty; }
        set
        {
            if (value == _myUiBoundProperty) return;
            _myUiBoundProperty = value;
            NotifyPropertyChanged(nameof(MyUiBoundProperty));
        }
    }

    public MyViewModelCtor(INavigationService navigationService) 
    {
       // Other code...
       BindingOperations.EnableCollectionSynchronization(AvailableDefectSubCategories, _lock );

    }

जानकारी के लिए कृपया https://msdn.microsoft.com/query/dev14.query?appId=Dev14IDEF1&l=EN-US&k=k(System.Windows.Data.BindingOperations.nableCollectionSynchronization);k(TargetFrameworkMoniker-.NETFERT पर देखें। % 3Dv4.7); k (DevLang-कोई तिथि नहीं) & rd सच =

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.