【问题标题】:BackgroundWorker or (Dispatcher)operation already completed when doing ReportProgress在进行 ReportProgress 时,BackgroundWorker 或(Dispatcher)操作已经完成
【发布时间】:2010-11-18 17:33:09
【问题描述】:

我使用DispatcherTimer,因为我需要每隔几分钟进行一次操作。在此我调用BackgroundWorker 来完成我的工作,然后使用附加到计时器的调度程序来更新我的用户界面。我认为我遇到的错误与计时器有关,但我不确定。是调度员完成还是后台工作人员完成?如何在 foreach 中进行 ReportProgress?

错误:

这个操作已经有 OperationCompleted 调用它并 进一步调用是非法的。

当这样做时:

(sender as BackgroundWorker).ReportProgress((counterTotalSteps / 100) * counterOnStep);

这里是简化的:

DispatcherTimer dispTimer = new DispatcherTimer();
Dispatcher dispatcher = dispTimer.Dispatcher;
dispTimer.Tick +=  delegate {dispTimer_Tick(dispatcher); };
dispTimer.Interval = new TimeSpan(0, 0, 45);
dispTimer.Start();

private void DoWork(object sender,Dispatcher dispatcher)
{
    int counterTotalSteps = PartialEmployees.Count();
    int counterOnStep = 1;

    dispatcher.BeginInvoke(new Action(() =>
    {                
        AllEmployees.Clear();
        //calling the ReportProgress here works
        foreach (var item in PartialEmployees)
        {
            counterOnStep ++;
            //part below throws the error
            (sender as BackgroundWorker).ReportProgress((counterTotalSteps / 100) *      counterOnStep); 
             AllEmployees.Add(item);                    
        }
        counterOnStep = 0;              
    }));           
}

编辑: 堆栈跟踪:

 at System.ComponentModel.AsyncOperation.VerifyNotCompleted()
   at System.ComponentModel.AsyncOperation.Post(SendOrPostCallback d, Object arg)
   at System.ComponentModel.BackgroundWorker.ReportProgress(Int32 percentProgress, Object userState)
   at System.ComponentModel.BackgroundWorker.ReportProgress(Int32 percentProgress)
   at testDispatcher.ViewModel.EmployeeListViewModel.<>c__DisplayClass7.<DoWork>b__6() in C:\Users\kozaj\Documents\Visual Studio 2010\Projects\testDispatcher\testDispatcher\ViewModel\EmployeeListViewModel.cs:line 91
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at System.Threading.ExecutionContext.runTryCode(Object userData)
   at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(Object source, Delegate method, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.InvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run(Window window)
   at System.Windows.Application.Run()
   at testDispatcher.App.Main() in C:\Users\kozaj\Documents\Visual Studio 2010\Projects\testDispatcher\testDispatcher\obj\x86\Debug\App.g.cs:line 50
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()

【问题讨论】:

  • 你为什么使用 BackgroundWorker,却在 UI 线程上完成所有工作?
  • 这只是示例代码,看看我是否能让它在其他事情中实现之前正常工作。你会移动到调度器之外的哪个部分?这些列表也绑定到 UI。

标签: c# mvvm backgroundworker wpf-4.0 dispatchertimer


【解决方案1】:

事件顺序

  1. DoWork 被调用
  2. DoWork 放置“AllEmployees.Clear();”进入调度队列
  3. DoWork 完成
  4. 调度员看到“AllEmployees.Clear();”并开始处理该函数。

我建议仅在实际具有 UI 交互的步骤上使用 dispatcher.Invoke(立即运行它)。

【讨论】:

  • 使用 .Invoke() 效果很好。我正在做的是增加一个progressBar,但它似乎一次完成(直接到100%)。当我在 WorkerProgressChanged() 上放置一个断点时,它不会在 foreach 调用它时被正确调用,而是在 foreach 之后调用它被调用多少次 - 或者看起来是这样。有什么建议吗?
  • ReportProgress 内部调用BeginInvoke 报告进度。它发布的消息仅在您完成后 UI 线程空闲时才会处理。
【解决方案2】:

由于您的工作是在 UI 线程上完成的,因此您根本不应该使用 BackgroundWorker。

相反,您应该直接在循环内更新进度条。

【讨论】:

  • 当你说我的工作是在 UI 线程上完成时,你是说因为 dispTimer_Tick() 在 UI 线程上,所以当它创建 BackgroundWorker 时它也在 UI 线程上?
  • @Lobal:BackgroundWorker 将在后台线程上运行。但是,您认为dispatcher.BeginInvoke 会做什么?
  • 将动作放入 UI 线程队列。我需要在那里进行循环,因为这些列表绑定到 UI,我不确定我还能如何“刷新”这些列表。对不起,如果我的工作马虎,我是一个非常新的编码员。
  • @Lobal:正确。因此,后台线程本身发生的所有事情都是 BeginInvoke 调用本身。你没有从 BackgroundWorker 中得到任何好处。
  • 我应该改变什么才能真正使用 BackgroundWorker?在我的实际项目中,我将进行大量列表操作,并且我知道我需要正确使用 BackgroundWorker。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多