调用线程无法访问此对象,因为另一个线程拥有它

Kun*_*esh 310 c# wpf multithreading backgroundworker

我的代码如下

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

objUDMCountryStandards.Country = txtSearchCountry.Text.Trim() != string.Empty ? txtSearchCountry.Text : null;获取网格数据的步骤会引发异常

调用线程无法访问此对象,因为另一个线程拥有它.

这有什么不对?

Can*_*ide 638

这是人们入门的常见问题.每当您从主线程以外的线程更新UI元素时,您需要使用:

this.Dispatcher.Invoke(() =>
{
    ...// your code here.
});
Run Code Online (Sandbox Code Playgroud)

您还可以使用control.Dispatcher.CheckAccess()检查当前线程是否拥有该控件.如果它拥有它,您的代码看起来很正常.否则,请使用上述模式.

  • `Application.Current.Dispatcher.Invoke(MyMethod,DispatcherPriority.ContextIdle);`以获取调度程序(如果不是在UI线程上)[this answer](http://stackoverflow.com/a/16904930/345659) (28认同)
  • +1。哈!我将它用于一些 WPF 黑客以保持解耦。我在一个静态上下文中,所以我不能使用 `this.Dispatcher.Invoke` .... 相反...... `myControl.Dispatcher.Invoke` :) 我需要返回一个对象,所以我做了 `myControlDispatcher.Invoke &lt;object&gt;(() =&gt; myControl.DataContext)`; (4认同)
  • 我和OP有同样的问题; 我现在的问题是该事件现在导致堆栈溢出.:\ (3认同)
  • 如果我没有错,你甚至无法从非所有者线程中读取UI对象; 让我感到有些惊讶. (3认同)
  • 回到我的旧项目并解决了这个问题。另外,我忘了为此 +1。这个方法效果很好!只需使用线程加载我们的本地化资源,它就能将我的应用程序加载时间缩短 10 秒甚至更多。干杯! (2认同)
  • @Candide我仍然收到此错误 (2认同)

小智 48

另一个很好的用途Dispatcher.Invoke是在执行其他任务的函数中立即更新UI:

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

我用它将按钮文本更新为" Processing ... "并在发出WebClient请求时禁用它.

  • Meta正在讨论这个答案.https://meta.stackoverflow.com/questions/361844/what-to-do-about-answers-which-are-highly-upvoted-but-dont-technically-answer-t?cb=1 (4认同)

dot*_*NET 38

要增加2美分,即使您通过代码调用,也会发生异常System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke().
问题的关键是,你必须调用Invoke()Dispatcher的的,你要访问的控制,在某些情况下可能不一样System.Windows.Threading.Dispatcher.CurrentDispatcher.所以相反,你应该使用YourControl.Dispatcher.Invoke()安全.在我意识到这一点之前,我正在敲打我的头几个小时.

  • @ l33t:WPF在一个应用程序中支持多个UI线程,每个线程都有自己的`Dispatcher`.在那些情况下(这是罕见的),调用`Control.Dispatcher`是安全的方法.仅供参考,你可以看到[本条](https://eprystupa.wordpress.com/2008/07/28/running-wpf-application-with-multiple-ui-threads/),以及[本SO职位( http://stackoverflow.com/questions/4620818/wpf-threading-dispatcher-static-vs-dispatcher-on-a-control)(特别是章鱼哥的回答). (4认同)
  • @aaronburro:问题是VM可能想要在备用线程上启动操作(例如任务、基于计时器的操作、并行查询),并且随着操作的进行,可能想要更新UI(通过RaisePropertyChanged等),这将依次尝试从非 UI 线程访问 UI 控件,从而导致此异常。我不知道*正确的 MVVM 方法*可以解决这个问题。 (2认同)

juF*_*uFo 31

如果有人试图BitmapSource在WPF和线程中使用并且有相同的消息:ImageSource在传递Freeze()一个线程参数之前先调用方法.

  • 啊,没有什么比解决一个没人理解的东西的古老模糊和神秘的伎俩. (20认同)
  • 我希望获得更多信息,以了解其工作原理以及如何自己解决问题。 (2认同)

Bas*_*ANI 24

这发生在我身上,因为我试图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);
}
Run Code Online (Sandbox Code Playgroud)

要解决这个问题,请将任何ui调用包含在Candide上面提到的答案中

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 );
}
Run Code Online (Sandbox Code Playgroud)

  • 赞成,因为这***不是***重复的答案或抄袭,但它提供了一个很好的例子,表明其他答案缺乏,同时对之前发布的内容给予认可。 (2认同)

Sar*_*rah 14

由于某种原因,Candide的答案没有建立.不过,这很有帮助,因为它让我找到了这个,它完美地工作:

System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke((Action)(() =>
    {
       //your code here...
    }));
Run Code Online (Sandbox Code Playgroud)

  • 如果它适合您,则首先不必使用它.`System.Windows.Threading.Dispatcher.CurrentDispatcher`是*当前线程*的调度程序.这意味着如果你在后台线程,它不会**将成为UI线程的调度程序.要访问UI线程的调度程序,请使用`System.Windows.Application.Current.Dispatcher`. (4认同)

小智 13

您需要更新到UI,所以使用

Dispatcher.BeginInvoke(new Action(() => {GetGridData(null, 0)})); 
Run Code Online (Sandbox Code Playgroud)


nPc*_*omp 6

这对我有用。

new Thread(() =>
        {

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

          //Your Code here.

        }, null);
        }).Start();
Run Code Online (Sandbox Code Playgroud)


Pau*_*mma 5

我还发现System.Windows.Threading.Dispatcher.CurrentDispatcher.Invoke()并不总是目标控制的调度程序,正如 dotNet 在他的回答中所写的那样。我无法访问控件自己的调度程序,因此我使用了它Application.Current.Dispatcher,它解决了问题。


Jes*_*aya 5

正如这里提到的,Dispatcher.Invoke可能会冻结 UI。应Dispatcher.BeginInvoke改为使用。

这是一个方便的扩展类,用于简化检查和调用调度程序调用。

示例用法:(从 WPF 窗口调用)

this Dispatcher.InvokeIfRequired(new Action(() =>
{
    logTextbox.AppendText(message);
    logTextbox.ScrollToEnd();
}));
Run Code Online (Sandbox Code Playgroud)

扩展类:

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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)