c# - 我可以使 WinForms OpenFileDialog.ShowDialog 不产生主线程吗?

标签 c# multithreading winforms callback dispatcher

我有一个 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 允许执行其他挂起的主线程回调?

更新: 我修改了上面的代码以更准确地反射(reflect)我的实际应用程序中发生的情况。所有回调操作都需要排队到我创建的 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防止另一个线程读取 wasDialogShown 在您阅读之后但在您将其设置为 true 之前。


更新(响应您的更新)

解决方案可能是拥有 2 个状态变量:dialogLaunchedconversionWorkerLaunched。第一个返回任务启动对话框。此任务和在对话启动后但在转换工作器启动之前完成的任务被添加到队列中。当对话框关闭时,转换开始,所有排队的任务都被添加到其中。转换启动后返回的任务会立即添加到工作线程。

关于c# - 我可以使 WinForms OpenFileDialog.ShowDialog 不产生主线程吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33067573/

相关文章:

c# - 如何在 C# 9 中将记录转换为元组

c# - DataGridView 中的超链接

c# - 在同一行有多个工具条

.net - 使用 __declspec(thread) 的线程本地存储在 C++/CLI 中失败

c# - 消息框关闭表单不正常

java - 找不到与给定名称匹配的资源,我哪里做错了?

c# - 如何在 dotnet 中将 ""解析为 long ("0")

c# - F# 与 C# 类互操作,该类具有一个可选的可空参数设置为除 null 之外的任何值会导致 NullReferenceException/AccessViolationException

c# - 如何让静态类不断更新自己的变量?

java - 在java中通过两个线程执行两个同步方法