"Operation already completed" 使用进度条时出错
"Operation already completed" error when using Progress Bar
我目前有:
查看模型
MovieProcessor movieProcessor = new MovieProcessor(SelectedPath, this);
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += movieProcessor.processMovie_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
progressBar.Show();
worker.RunWorkerAsync();
worker_ProgressChanged
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
CurrentProgress = e.ProgressPercentage;
}
在我的 MovieProcessor
class 中,我有方法 processMovie_DoWork
.
public async void processMovie_DoWork(object sender, DoWorkEventArgs e)
{
for (int x = 0; x < totalFilesToProcess; ++x)
{
// Do stuff like calling API
(sender as BackgroundWorker).ReportProgress(x);
}
}
第二次调用ReportProgress(x)
,报错:
This operation has already had OperationCompleted called on it and further calls are illegal.
CurrentProgress
绑定到我的 XAML
<ProgressBar Minimum="0" Maximum="{Binding MaxValueProgressBar}" Value="{Binding CurrentProgress, Mode=OneWay}" />
有人知道这里会发生什么吗?
详细说明 dkozl 的评论:
可能是 async
导致了问题。您发布的代码中没有任何内容会导致问题,但当然您发布的代码示例远未完成。
如果你的 processMovie_DoWork()
方法中有一个 await
语句(这是创建方法 async
的通常原因),那么该方法只会执行到第一个await
语句的点,然后它会退出。
就BackgroundWorker
class而言,这标志着工作的结束。它无法知道是否会调用某些延续。因此,当您调用 ReportProgress()
方法时,操作实际上已经完成,因此对 ReportProgress()
的调用是非法的。
这里有几个选项:
- 删除
await
语句并同步执行这些操作。最好通过调用 API. 的同步版本
- 摆脱
BackgroundWorker
并直接调用您的 processMovie_DoWork()
方法(尽管可能重命名为其他方法)。在这种情况下,您无需调用 ReportProgress()
方法,只需直接更新 CurrentProgress
属性。
恕我直言,第二种选择更可取。您可以简单地 await
您的 processMovie_DoWork()
方法,并避免处理 BackgroundWorker
.
的所有麻烦
我刚刚在尝试使用后台工作程序从 Web API 轮询状态时遇到了这个问题。
我通过删除 async
并将 await
运算符更改为 Task.Wait()
来解决此问题,并且效果很好。
这是我的代码代替“// Do stuff like calling API
”:
var task = JobManager.GetJobStatus(id);
task.Wait();
var status = task.Result;
我希望这对其他人有帮助。我相信你已经解决了这个问题。
我目前有:
查看模型
MovieProcessor movieProcessor = new MovieProcessor(SelectedPath, this);
BackgroundWorker worker = new BackgroundWorker();
worker.WorkerReportsProgress = true;
worker.DoWork += movieProcessor.processMovie_DoWork;
worker.ProgressChanged += worker_ProgressChanged;
progressBar.Show();
worker.RunWorkerAsync();
worker_ProgressChanged
void worker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
CurrentProgress = e.ProgressPercentage;
}
在我的 MovieProcessor
class 中,我有方法 processMovie_DoWork
.
public async void processMovie_DoWork(object sender, DoWorkEventArgs e)
{
for (int x = 0; x < totalFilesToProcess; ++x)
{
// Do stuff like calling API
(sender as BackgroundWorker).ReportProgress(x);
}
}
第二次调用ReportProgress(x)
,报错:
This operation has already had OperationCompleted called on it and further calls are illegal.
CurrentProgress
绑定到我的 XAML
<ProgressBar Minimum="0" Maximum="{Binding MaxValueProgressBar}" Value="{Binding CurrentProgress, Mode=OneWay}" />
有人知道这里会发生什么吗?
详细说明 dkozl 的评论:
可能是 async
导致了问题。您发布的代码中没有任何内容会导致问题,但当然您发布的代码示例远未完成。
如果你的 processMovie_DoWork()
方法中有一个 await
语句(这是创建方法 async
的通常原因),那么该方法只会执行到第一个await
语句的点,然后它会退出。
就BackgroundWorker
class而言,这标志着工作的结束。它无法知道是否会调用某些延续。因此,当您调用 ReportProgress()
方法时,操作实际上已经完成,因此对 ReportProgress()
的调用是非法的。
这里有几个选项:
- 删除
await
语句并同步执行这些操作。最好通过调用 API. 的同步版本
- 摆脱
BackgroundWorker
并直接调用您的processMovie_DoWork()
方法(尽管可能重命名为其他方法)。在这种情况下,您无需调用ReportProgress()
方法,只需直接更新CurrentProgress
属性。
恕我直言,第二种选择更可取。您可以简单地 await
您的 processMovie_DoWork()
方法,并避免处理 BackgroundWorker
.
我刚刚在尝试使用后台工作程序从 Web API 轮询状态时遇到了这个问题。
我通过删除 async
并将 await
运算符更改为 Task.Wait()
来解决此问题,并且效果很好。
这是我的代码代替“// Do stuff like calling API
”:
var task = JobManager.GetJobStatus(id);
task.Wait();
var status = task.Result;
我希望这对其他人有帮助。我相信你已经解决了这个问题。