3

私のアプリケーションでは、リストボックスに追加する前に、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 が終了するまで待ってから新しいファイルを開くにはどうすればよいですか?

4

2 に答える 2

9

私が理解しているように、一度に起動するバックグラウンド ワーカーは 1 つだけです。もしそうなら、これを試してください( 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 を 1 つずつ開始します。これは最適ではない可能性があります (BackgroundWorker をまったく回避し、threadStartデリゲートの 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 を参照):

  • 仕事
  • TaskScheduler.FromCurrentSynchronizationContext
  • Parallel.ForEach メソッド (IEnumerable、ParallelOptions、Action)
于 2013-05-28T12:01:15.307 に答える
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);
    }
}
于 2013-05-28T10:51:36.773 に答える