C# 调用线程无法访问此对象,因为其他线程拥有它

C# 调用线程无法访问此对象,因为其他线程拥有它,c#,wpf,multithreading,C#,Wpf,Multithreading,我读过很多关于这个错误的帖子,但我不明白如何在我的解决方案中解决这个问题((我有一个progressbar对话框,里面有一些逻辑,它是通过ButtonClick从大型机调用的) void OnBtnClick(object sender, RoutedEventArgs e) { ProgressDialog dlg = new ProgressDialog(""); dlg.Closing += new CancelEventHandler(dlg_Closing);

我读过很多关于这个错误的帖子,但我不明白如何在我的解决方案中解决这个问题((我有一个progressbar对话框,里面有一些逻辑,它是通过ButtonClick从大型机调用的)

void OnBtnClick(object sender, RoutedEventArgs e)
{  
    ProgressDialog dlg = new ProgressDialog("");
    dlg.Closing += new CancelEventHandler(dlg_Closing);
    dlg.Closed += new EventHandler(dlg_Closed);
    //dlg.AutoIncrementInterval = 0;
    LibWrap lwrap = new LibWrap();
    DoWorkEventHandler handler = delegate
    {
        BitmapFrame bf = wrap.engine(BitmapFrame.Create(FXPhotoStudio.App
                                                            .draggedImage),
                                     this.fxPSEditorView); 
    };
    dlg.CurrentLibWrap = lwrap;
    dlg.AutoIncrementInterval = 100;
    dlg.IsCancellingEnabled = true;
    dlg.Owner = Application.Current.MainWindow;
    dlg.RunWorkerThread(0, handler); 
}
对于此进度条对话框,在关闭事件上的同一类(大型机)中还有一个处理程序

void dlg_Closed(object sender, EventArgs e)
{
    try
    { 
        mainFrameView.CurrentImage = effectedImage;//!error here!
    }
}
EffecteImage是大型机的一个字段。它由my ProgressDialog设置。 我在ProgressDialog.cs中做了以下操作:

(this.Owner as MainFrame).effectedImage = currentLibVrap.GetEffectedImage;
currentLibVrap
是在点击
OnBtnClick
中设置的-参见上文 谁能帮我解决这个问题

这是关闭ProgressBarDialog的代码:

    private void worker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
      if (!Dispatcher.CheckAccess())
      {
        //run on UI thread
        RunWorkerCompletedEventHandler handler = worker_RunWorkerCompleted;
        Dispatcher.Invoke(DispatcherPriority.SystemIdle, handler, new object[] {sender, e}, null);
        return;
      }

      if (e.Error != null)
      {
        error = e.Error;
      }
      else if (!e.Cancelled)
      {
        //assign result if there was neither exception nor cancel

          (this.Owner as MainWindow).effectedImage = currentLibVrap.GetEffectedImage;//! ok there

        result = e.Result;
      }

  //update UI in case closing the dialog takes a moment
 // progressTimer.Stop();
  progressBar.Value = progressBar.Maximum;
  btnCancel.IsEnabled = false;

  //set the dialog result, which closes the dialog
  DialogResult = error == null && !e.Cancelled;
}
还有一个工作流程:

/// Launches a worker thread which is intended to perform
/// work while progress is indicated, and displays the dialog
/// modally in order to block the calling thread.
/// </summary>
/// <param name="argument">A custom object which will be
/// submitted in the <see cref="DoWorkEventArgs.Argument"/>
/// property <paramref name="workHandler"/> callback method.</param>
/// <param name="workHandler">A callback method which is
/// being invoked on a background thread in order to perform
/// the work to be performed.</param>


   public bool RunWorkerThread(object argument, DoWorkEventHandler workHandler)
    {
      if (autoIncrementInterval.HasValue)
      {
        //run timer to increment progress bar
        progressTimer.Interval = TimeSpan.FromMilliseconds(autoIncrementInterval.Value);
        progressTimer.Start();
         // LibWrap lwrap = new LibWrap();
         // BitmapFrame bf = lwrap.engine(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));//(aa.Image);

      }

      //store the UI culture
      uiCulture = CultureInfo.CurrentUICulture;

      //store reference to callback handler and launch worker thread
      workerCallback = workHandler;
      worker.RunWorkerAsync(argument);

      //display modal dialog (blocks caller)
      return ShowDialog() ?? false;
    }

/// <summary>
/// Worker method that gets called from a worker thread.
/// Synchronously calls event listeners that may handle
/// the work load.
/// </summary>
private void worker_DoWork(object sender, DoWorkEventArgs e)
{
  try
  {
    //make sure the UI culture is properly set on the worker thread
    Thread.CurrentThread.CurrentUICulture = uiCulture;
    //invoke the callback method with the designated argument
    workerCallback(sender, e);
  }
  catch (Exception)
  {
    //disable cancelling and rethrow the exception
    //Dispatcher.BeginInvoke(DispatcherPriority.Normal,
     //                      (SendOrPostCallback) delegate { btnCancel.SetValue(Button.IsEnabledProperty, false); },
    //                       null);
    return;
    //throw;
  }
}
///启动要执行的工作线程
///指示进度时工作,并显示对话框
///modally以阻止调用线程。
/// 
///一个自定义对象,它将
///提交于
///属性回调方法。
///一个回调方法
///在后台线程上被调用以执行
///要执行的工作。
public bool RunWorkerThread(对象参数,DoWorkEventHandler workHandler)
{
if(自动递增间隔.HasValue)
{
//运行计时器以增加进度条
progressTimer.Interval=TimeSpan.FromMillions(autoIncrementInterval.Value);
progressTimer.Start();
//LibWrap lwrap=新的LibWrap();
//BitmapFrame bf=lwrap.engine(BitmapFrame.Create(FXPhotoStudio.App.draggedImage));/(aa.Image);
}
//存储UI区域性
uiCulture=CultureInfo.CurrentUICulture;
//存储对回调处理程序的引用并启动工作线程
workerCallback=工作处理程序;
worker.RunWorkerAsync(参数);
//显示模式对话框(阻止调用者)
返回ShowDialog()??false;
}
/// 
///从工作线程调用的工作线程方法。
///同步调用可能处理
///工作量很大。
/// 
私有void worker_DoWork(对象发送方,DoWorkEventArgs e)
{
尝试
{
//确保在工作线程上正确设置了UI区域性
Thread.CurrentThread.CurrentUICulture=uiCulture;
//使用指定的参数调用回调方法
workerCallback(发送方,e);
}
捕获(例外)
{
//禁用取消并重新显示异常
//Dispatcher.BeginInvoke(DispatcherPriority.Normal,
//(SendOrPostCallback)委托{btnCancel.SetValue(Button.IsEnabledProperty,false);},
//无效);
返回;
//投掷;
}
}

您需要使用(立即分派并返回)或(在分派操作之前阻塞)将其分派到UI线程

这是因为,默认情况下,应用程序的线程模型是单线程单元(single-threaded plant,STA)。这意味着,只有创建UI元素的线程才能与其交互,其他想要对UI元素执行操作的线程必须将其对该元素的操作分派给UI线程

void dlg_Closed(object sender, EventArgs e)
{
    try
    { 
        mainFrameView.CurrentImage = effectedImage;//!error here!
    }
}
是否确定UI线程发送此事件?是否尝试分派设置CurrentImage? 你能不能简化你的代码,只留下相关的方法

UPD:我的意思是,你有试过发送当前图像设置吗

    Application.Current.MainWindow.Dispatcher.BeginInvoke(new Action(()=>
                                          {
                                              mainFrameView.CurrentImage = effectedImage;
                                          }));

您可以使用Dispather.Invoke或Dispatcher.BeginInvoke。它们都将对UI线程的调用进行封送处理(这就是您的错误所在),BeginInvoke设计用于在后台线程中运行繁重的操作,而Invoke只是一个封送处理程序,因此对于您的任务类型,我将坚持使用最后一个

下面是您的操作方法(假设
mainFrameView.CurrentImage
Image
类型,否则只需更改为任何类型即可):

C#

更新1为参数使用唯一的名称,以避免与现有变量名称交叉

mainFrameView.Dispatcher.Invoke(new Action<object>((myImage2012) => 
{ mainFrameView.CurrentImage = (Image)myImage2012; }), 
new object[1] { effectedImage });
mainFrameView.Dispatcher.Invoke(新操作((myImage2012)=>
{mainFrameView.CurrentImage=(图像)myImage2012;}),
新对象[1]{effectedImage});

您需要调用
Invoke()
来调用UI线程中的函数。我尝试在您编写时分派CurrentImage设置,但它落在mainFrameView.CurrentImage中,并出现相同的错误((Invoke和BeginInvoke与短作业或长作业无关,而是在同步或异步调用上。长作业最好不要在UI线程上运行(而不是从Dispatcher调用),因为它将冻结UI。