等待我的后台工作程序完成后再打开新的后台工作程序

3

在我的应用程序中,我在将文件添加到我的 Listbox 之前通过打开 Wireshark 进程来检查它们。这是“添加目录”单击事件,它获取根文件夹并检查此文件夹及其子文件夹中的所有文件:

private void btnAddDir_Click(object sender, EventArgs e)
{
    try
    {
        if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
        {
            ThreadStart threadStart = delegate
            {
                foreach (string file in SafeFileEnumerator.EnumerateFiles(folderBrowserDialog1.SelectedPath, "*.*", SearchOption.AllDirectories))
                {
                    Interlocked.Increment(ref numWorkers);
                    StartBackgroundFileChecker(file);
                }
            };

            Thread thread = new Thread(threadStart);
            thread.IsBackground = true;
            thread.Start();
        }
    }
    catch (Exception)
    { }
}

private void StartBackgroundFileChecker(string file)
{
    ListboxFile listboxFile = new ListboxFile();
    listboxFile.OnFileAddEvent += listboxFile_OnFileAddEvent;
    BackgroundWorker backgroundWorker = new BackgroundWorker();
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.DoWork +=
    (s3, e3) =>
    {
        //check my file
    };

    backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
    backgroundWorker.RunWorkerAsync();
}

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (Interlocked.Decrement(ref numWorkers) == 0)
    {
        //update my UI
    }
}

当我检查这个文件时,我打开了Wireshark进程,所以如果我选择有许多文件的文件夹,会打开许多Wireshark进程,这会占用很多内存。如何等待我的BackgroundWorker完成后再打开新的进程?


我很确定我已经对这段代码进行了评论。完全相同的建议,将foreach循环移至worker内部,这样您只需要一个BGW。等待线程总是不好的想法,您的代码会死锁。 - Hans Passant
2个回答

9

根据我的理解,您希望在同一时间只启动单个后台工作程序。如果是这样,请尝试使用以下代码(基于System.Threading.AutoResetEvent):

//introduce additional field
private AutoResetEvent _workerCompleted = new AutoResetEvent(false);
//modify StartBackgroundFileChecker
private void StartBackgroundFileChecker(string file)
{
    ListboxFile listboxFile = new ListboxFile();
    listboxFile.OnFileAddEvent += listboxFile_OnFileAddEvent;
    BackgroundWorker backgroundWorker = new BackgroundWorker();
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.DoWork +=
    (s3, e3) =>
    {
        //check my file
    };

    backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
    backgroundWorker.RunWorkerAsync();
   //new code - wait for completion
   _workerCompleted.WaitOne();
}
//add completion notification to backgroundWorker_RunWorkerCompleted
void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (Interlocked.Decrement(ref numWorkers) == 0)
    {
        //update my UI
    }

    //new code - notify about completion
    _workerCompleted.Set();
}

在这种解决方案中,您的后台线程将逐个启动新的BackgroundWorker - 这可能不是最优的(您可以完全避免BackgroundWorker,并仅通过线程启动委托中的Dispatch更新UI)。
在我看来,更好的方法是控制并行线程的数量,并仍然使用有限数量的线程处理多个文件。
以下是替代解决方案(基于System.Threading.Tasks命名空间):
 private void btnAddDir_Click(object sender, EventArgs e)
 {
   var selectedPath = folderBrowserDialog1.SelectedPath;
   Task.Factory.StartNew(() =>
        {
           
            var files = Directory.EnumerateFiles(selectedPath, "*.*", SearchOption.AllDirectories);

            Parallel.ForEach(files,
                             new ParallelOptions
                             {
                                     MaxDegreeOfParallelism = 10 // limit number of parallel threads here 
                             },
                             file =>
                             {
                                 //process file here - launch your process
                             });
        }).ContinueWith(
            t => { /* when all files processed. Update your UI here */ }
            ,TaskScheduler.FromCurrentSynchronizationContext() // to ContinueWith (update UI) from UI thread
        );
}

您可以根据自己的特定需求调整此解决方案。

使用的类/方法(请参阅MSDN以获取参考):

  • Task
  • TaskScheduler.FromCurrentSynchronizationContext
  • Parallel.ForEach 方法 (IEnumerable, ParallelOptions, Action)

我认为控制并行数量会更好。 - user2214609

0
也许可以这样做,不要使用foreach,而是保持文件列表,并在完成后只取第一个元素并更新您的列表。
private List<string> _files;

private void btnAddDir_Click(object sender, EventArgs e)
{
    try
    {
        if (folderBrowserDialog1.ShowDialog() == DialogResult.OK)
        {

            _files = new List<string>(SafeFileEnumerator.EnumerateFiles(folderBrowserDialog1.SelectedPath, "*.*", SearchOption.AllDirectories));

            Interlocked.Increment(ref numWorkers);
            var file = _files.FirstOrDefault();
            if(file != null)
                StartBackgroundFileChecker(file);
        }
    }
    catch (Exception)
    { }
}

private void StartBackgroundFileChecker(string file)
{
    ListboxFile listboxFile = new ListboxFile();
    listboxFile.OnFileAddEvent += listboxFile_OnFileAddEvent;
    BackgroundWorker backgroundWorker = new BackgroundWorker();
    backgroundWorker.WorkerReportsProgress = true;
    backgroundWorker.DoWork +=
    (s3, e3) =>
    {
        //check my file
    };

    backgroundWorker.RunWorkerCompleted += backgroundWorker_RunWorkerCompleted;
    backgroundWorker.RunWorkerAsync();
}

void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
    if (Interlocked.Decrement(ref numWorkers) == 0)
    {
        //update my UI
        _files = _files.Skip(1);
        var file = _files.FirstOrDefault();
        if(file != null)
            StartBackgroundFileChecker(file);
    }
}

问题在于,我正在使用EnumerateFiles来获取所有“正在处理”的文件,我不想等到获取所有文件。 - user2214609
你可以将它转换为字符串列表,这样你就可以得到所有正在处理的文件,但是每个文件都将在Backgroundworker完成后进行处理。Backgroundworker在哪里打开wireshark? - jjchiw
Wireshark通过另一个类打开进程,该类中写有//检查我的文件。 - user2214609

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接