我可以让WinForms OpenFileDialog.ShowDialog不产生主线程吗?
本文关键字:线程 WinForms OpenFileDialog ShowDialog 我可以 | 更新日期: 2023-09-27 18:17:13
我有一个WinForms程序,该程序下载然后通过用于报告整体进度和作业完成情况的批处理线程在自己的线程上转换文件集合。 每个文件都将下载到自己的线程上,然后下载后,它将通过主线程通过第二个批处理线程传递到自己的转换线程。 通过主线程进行此传递的原因是,一旦下载了第一个文件,主线程可以提示用户输入所有这些文件的转换版本的保存位置。
我遇到的问题是 OpenFileDialog.ShowDialog(( 方法(用于从用户那里获取保存目录(阻止了第一个完成的下载线程的主线程回调。 这反过来又允许其他下载线程完成(不是问题(,但随后也开始执行对主线程的回调,此时它们都点击 OpenFileDialog.ShowDialog((,否则他们不会这样做,因为我的第二个批处理线程将在第一次转换中运行,因此它们将能够添加到批处理线程管理器中。
我正在使用 Dispatcher.CurrentDispatcher.BeginInvoke(myCallbackDelegate, DispatcherPriority.Normal, myParameter(,以便在项目工作完成后对主线程进行回调。 我在下面创建了一个简化的示例来演示问题代码。
public partial class TestForm : Form
{
private readonly Dispatcher dispatcher = Dispatcher.CurrentDispatcher;
//An instance of my own BatchBackgroundWorkerThread<T> class; this class parrarlelizes tasks and can report their individual and overall progress, completion and cancellation.
private readonly BatchBackgroundWorkerThread<int> conversionBatchBackgroundWorker = new BatchBackgroundWorkerThread<int>();
public TestForm()
{
InitializeComponent();
for (int threadIndex = 0; threadIndex < 4; threadIndex++)
{
new Thread(DoSomeBackgroundWork).Start(threadIndex);
Thread.Sleep(10);
}
}
private void DoSomeBackgroundWork(object threadIndex)
{
Thread.Sleep(1000);
dispatcher.BeginInvoke(new ParameterizedThreadStart(WorkCompletedCallback), DispatcherPriority.Normal, threadIndex);
}
private void WorkCompletedCallback(object threadIndex)
{
//Waits for CanQueueNewItems to be in a valid readable state.
conversionBatchBackgroundWorker.WaitForPendingRun();
//CanQueueNewItems is true when the batch background worker's batch thread has been launched and its internal CountdownEvent and cancellationState have been initialized.
if (conversionBatchBackgroundWorker.IsBusy && conversionBatchBackgroundWorker.CanQueueNewItems)
//Queues the item in the BatchBackgroundWorker for parrarelization with any other items.
//NOTE: This code is not currently hit unless the user makes a dialog selection before another callback can reach the above if statement.
conversionBatchBackgroundWorker.Additem(threadIndex);
else
{
FolderBrowserDialog.ShowDialog();
conversionBatchBackgroundWorker.RunWorkerAsync(new int[] { (int)threadIndex }, FolderBrowserDialog.SelectedPath);
}
}
}
我尝试将DispatcherPriority更改为DispatcherPriority.SystemIdle - 这使得所有挂起的ShowDialog对话框都出现,然后必须单击"确定"或"取消"到每个对话框,但除此之外结果仍然相同。
有没有办法阻止 ShowDialog 允许对主线程执行其他挂起的回调?
更新:我已经修改了上面的代码,以更接近地反映我的实际应用程序中发生的事情。 所有回调操作都需要排队到我创建的 BatchBackgroundWorker 类中,以便执行其相关转换。 这就是为什么我不能简单地在调用 ShowDialog(( 之前设置一个变量说它被调用了,因为其余的回调会尝试将自己排队到当前尚未启动的 BatchBackgroundWorker 中(此时 BatchBackgroundWorker 线程未运行,因为启动正在等待用户对对话框的响应(。
只需反转语句,以便在打开对话框之前设置标志。
wasDialogShown = true;
OpenFileDialog.ShowDialog();
您还应该锁定使用wasDialogShown
的代码部分:
private readonly object dialogLock = new Object();
private void WorkCompletedCallback(object threadIndex)
{
bool wasShown;
lock(dialogLock) {
wasShown = wasDialogShown;
wasDialogShown = true;
}
if (wasShown) {
MessageBox.Show("Success!");
} else {
OpenFileDialog.ShowDialog();
}
}
Lock 语句可防止另一个线程在您读取wasDialogShown
之后但在您将其设置为 true
之前读取它。
更新
(响应您的更新(
解决方案可能是有 2 个状态变量:dialogLaunched
和 conversionWorkerLaunched
。第一个返回任务将启动对话框。此任务和在启动对话框之后但在启动转换工作程序之前完成的任务将添加到队列中。当对话框关闭时,将启动转换,并将所有排队的任务添加到其中。启动转换后返回的任务将立即添加到工作人员中。