我有一个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 Statement会在您读取之后但在将其设置为true之前阻止另一个线程读取wasDialogShown.
更新(响应您的更新)
解决方案可能是有两个状态变量:dialogLaunched和conversionWorkerLaunched.第一个返回任务启动对话框.在启动对话框之后但在启动转换工作程序之前完成此任务和任务将添加到队列中.对话框关闭后,将启动转换并将所有排队的任务添加到其中.启动转换后返回的任务会立即添加到工作程序中.