成功运行BackgroundWorker() 后程序的其余部分不执行
Posted
技术标签:
【中文标题】成功运行BackgroundWorker() 后程序的其余部分不执行【英文标题】:After running successfully BackgroundWorker() the rest of the program is not executed 【发布时间】:2021-01-20 02:23:33 【问题描述】:我正在使用 BackgroundWorker() 在后台执行长时间运行的查询,同时我正在显示我的执行正在运行的弹出窗口。
这是我如何调用 bg_worker()
using System.Windows;
using System.ComponentModel;
using System.Threading;
using System;
using System.IO;
using System.Data.SqlClient;
using System.Windows.Input;
namespace TestEnvironment
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class ProgressBarTemplate : Window
private CreateProjectScreen _CreateProjectScreen;
private LoginScreen _LoginScreen;
public ProgressBarTemplate()
InitializeComponent();
public static int RunCalculationsMethod(string connectionstring, string foldername)
bool exists = Directory.Exists(foldername);
if (!exists)
Directory.CreateDirectory(foldername);
try
using (SqlConnection sqlConnection = new SqlConnection(connectionstring))
var calculations_query = "SELECT * FROM table1");
using SqlCommand sqlCommand = new SqlCommand(calculations_query, sqlConnection);
sqlConnection.Open();
sqlCommand.CommandTimeout = 60 * 10;
int NumbderOfRecords = sqlCommand.ExecuteNonQuery();
return NumbderOfRecords;
catch (Exception ex)
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return -100;
private void Window_ContentRendered(object sender, EventArgs e)
BackgroundWorker worker = new BackgroundWorker();
worker.DoWork += worker_DoWork;
worker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;
worker.RunWorkerAsync();
void worker_DoWork(object sender, DoWorkEventArgs e)
int IsSuccessful = RunCalculationsMethod("Server=localhost;Database=DB_Name;Integrated Security=SSPI", String.Format("C:\\folder_path\\"));
void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
// This is called on the UI thread when the DoWork method completes
// so it's a good place to hide busy indicators, or put clean up code
try
this.Close();
MessageBox.Show("DQ Calculations completed successfully", "Information", MessageBoxButton.OK, MessageBoxImage.Information);
catch (Exception ex)
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
上面的代码放在一个叫做 ProgressBarTemplate() 的窗口中
我想要的是在按钮点击时调用 background_worker,在我的 MainWindow 中放置一个按钮
所以我的 MainWindow 有以下按钮点击
private void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
//RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_Findings_0", HomePageTab.Header.ToString().Split(" - ")[1]));
try
Application.Current.Dispatcher.Invoke((Action)delegate
ProgressBarTemplate win_progressbar = new ProgressBarTemplate();
win_progressbar.Show();
//RunCalculationsMethod(SQLServerConnectionDetails(), String.Format("C:\\DQ_folder_test\\Findings\\"));
); // The code runs up to this point.
//The code below is not executed for a reason, which I am trying to solve with this question
List<SucessfulCompletion> reportsucessfulcompletion = new List<SucessfulCompletion>();
reportsucessfulcompletion = SuccessfulCalculationsTimestamp(SQLServerConnectionDetails());
if (reportsucessfulcompletion[0].Result==1)
//Enable is only if successfull
PreviewCalculationsButton.IsEnabled = true;
PreviewReportButton.IsEnabled = true;
//add textbox of sucess
TickButtonSymbolCalculations.Visibility = Visibility.Visible;
SQLSuccessfulTextCalculations.Visibility = Visibility.Visible;
XerrorSymbolCalculations.Visibility = Visibility.Hidden;
SQLFailedTextCalculations.Visibility = Visibility.Hidden;
SQLSuccessfulTextCalculations.Text = String.Format("Completed On: 0", reportsucessfulcompletion[0].Timestampvalue);
else
//add textbox of fail
TickButtonSymbolCalculations.Visibility = Visibility.Hidden;
SQLSuccessfulTextCalculations.Visibility = Visibility.Hidden;
XerrorSymbolCalculations.Visibility = Visibility.Visible;
SQLFailedTextCalculations.Visibility = Visibility.Visible;
SQLFailedTextCalculations.Text = String.Format("Failed On: 0", reportsucessfulcompletion[0].Timestampvalue);
catch (Exception ex)
MessageBox.Show(ex.ToString(), "Error", MessageBoxButton.OK, MessageBoxImage.Error);
return;
单击按钮时,我通过调用窗口 ProgressBarTemplate() 来启动 bg_worder。尽管在完成任务后代码生成一些文本并启用某些按钮的可见性,但它们并没有被执行。为什么会这样?我错过了什么吗?
【问题讨论】:
这是可怕的代码。我不知道 UI 何时结束,线程逻辑何时开始。您的代码可能会在调用方法(即 UI 线程)中完成所有操作。 @Bizhan 问题出在我的第一个代码 sn-p(又名 BG Worker)还是关于 MainWindow(第二个代码 sn-p)? :) 我认为您不应该通过调度程序创建进度条。您已经在按钮单击事件处理程序的 UI 线程中,因此您应该能够直接创建它。 @allan 如果你提到Application.Current.Dispatcher.Invoke((Action)delegate ...);
我这样做是因为 C# 给我一个关于 STA 线程错误的错误
@NikSp 忘记 BGW。这是一个自 2012 年以来完全被 Task.Run
取代的过时类。与其将计算放在进度窗口中,不如在 click
或 rendered
处理程序中使用简单的 var results=await Task.Run(()=>SomeHeavyComputing());
就足够了,允许您在之前更新 UI 和在没有 Invoke
的异步操作之后
【参考方案1】:
代码有点不清楚,所以我将发布它应该如何完成。
BGW 是一个过时的类,自 2012 年起被 Task.Run
和 Progress< T> 完全取代。当 async/await
可用时,无需使用 BGW 或 Invoke
。
您可以使用异步事件处理程序,在后台执行任何计算并在后台操作完成后更新 UI,而不是将计算放在进度窗口中。进度表似乎没有报告任何进度,所以只需要显示和隐藏它。代码可以很简单:
private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
var win_progressbar = new ProgressBarTemplate();
win_progressbar.Show();
try
//Update the UI
var results=await Task.Run(()=> RunCalculationsMethod(...));
//Update the UI
finally
win_progressbar.Close();
try/finally
用于确保即使出现错误也关闭表单。
进度报告
进度报告可通过Progress 类和IProgress 接口获得。 IProgress<T>
允许后台任务向实现接口的类发送强类型消息。 Progress<T>
实现确保消息在创建它的线程中处理,通常是 UI 线程。
假设这是消息类型:
class MyProgress
public int Percentget;set;
public string Message get;set;
RunCalculationsMethod
可以修改为接受和使用IProgress<MyProgress>
;
public static int RunCalculationsMethod(string connectionstring, string foldername,
IProgress<MyProgress> progress)
progress.Report(new MyProgressPercent=0,Message="Starting";
....
progress.Report(new MyProgressPercent=100,Message="Finished";
事件处理程序只需要创建一个Progress<MyProgress>
并提供一个更新 UI 的方法。假设ProgressBarTemplate
有这样一个方法,称为Update(string,int)
:
private async void RunCalculationsButton_Click(object sender, RoutedEventArgs e)
var win_progressbar = new ProgressBarTemplate();
IProgress<MyProgress> pg=new Progress<MyProgress>(pg=>
win_progressbar.Update(pg.Message,pg.Percent));
win_progressbar.Show();
try
//Update the UI
var results=await Task.Run(()=> RunCalculationsMethod(...,pg));
//Update the UI
finally
win_progressbar.Close();
你可以在Async in 4.5: Enabling Progress and Cancellation in Async APIs找到更详尽的解释
【讨论】:
Panagiotis 求答案。如果我采用这种方法,那么有一个进度条有什么意义呢?我的意思是因为我从主窗口而不是从 ProgressBar 模板窗口调用 RunCalculationsMethod()。还有如何使用Progress<T>
?
@NikSp 我更新了答案,显示如何使用Progress<T >
并链接到解释进度报告和取消的文章。进度表只能用于显示进度。这样它就可以被应用程序中的任何异步操作重用。
好的,现在我的 ProgressBarTemplate 窗口有这个代码void worker_DoWork(object sender, DoWorkEventArgs e) int IsSuccessful = RunCalculationsMethod("Server=localhost;Database=ΓειαΣου;Integrated Security=SSPI", String.Format("folder_path"));
我应该删除它吗?
从该表单中删除它。从事件处理程序调用它。进度表应该只显示进度。并彻底移除 BGW
是的。顺便说一句,既然您是从数据科学开始的,请查看 Parallel and Asynchronous Programming 和 The Server-SIde Story: Παράλληλος και ασύγχρονος προγραμματισμός στο .NET。并行、并发和异步是完全不同的野兽,需要不同的类【参考方案2】:
您应该使用 Application.Current.Dispatcher.Invoke,因为您尝试访问 UI 元素。除主线程外,其他线程无法访问ui元素。
将您的代码替换为
Dispatcher.Invoke(() =>
// UI access code
);
【讨论】:
或者只使用async/await
所以不需要Invoke
@PanagiotisKanavos 通常,您的答案是收集。但有时在使用嵌套 async/await 时不起作用。
它一直在工作。当它看起来没有时,它是一个编码错误,例如有人试图从异步方法中更新 UI,或者使用 ConfigureAwait(false)
没有意识到它做了什么
@PanagiotisKanavos 例如。像这个按钮示例。 private async void OnButtonClicked(object sende, RoutedEventArgs e) 此区域可以进行 UI 访问。 await Task.run(async() => 此区域无法访问 UI。
这是一个编码错误。您正在尝试从后台线程更新 UI。如果您想从后台线程中报告进度,请使用IProgress<T>
接口。自 2012 年起不再需要 Invoke
以上是关于成功运行BackgroundWorker() 后程序的其余部分不执行的主要内容,如果未能解决你的问题,请参考以下文章
为啥 BackgroundWorker 的 Progress 事件运行在不同的线程上?
从 BackgroundWorker 运行时 CrystalReportViewer.RefreshReport 挂起