使用C#Tasks运行用于在整个系统中搜索文件的程序时,UI会冻结

时间:2011-07-13 23:40:00

标签: c#

我编写了一个小程序,在整个syatem中搜索某种文件,搜索完成后会在列表视图中显示结果,但是当我运行此程序时,我的表单冻结,我无法做任何事情。

有人可以帮助我解决问题,下面是我编写的代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
using System.IO.Compression;
using System.Security.AccessControl;


namespace SearchAndZipUtility
{
    public partial class MainForm : Form
    {
        private string _destFolder;
        private string _sourceToSearch = @"E:\New Books";
        private TaskScheduler schedular = TaskScheduler.Current;
        private CancellationTokenSource _ct = new CancellationTokenSource();
        private List<string> files = new List<string>();
        private string _selectedFileType;

        public MainForm()
        {
            InitializeComponent();
            toolStripStatusInfo.Visible = false;
            btnZip.Enabled = false;
        }

        protected override void OnLoad(EventArgs e)
        {
            // bind file types to combo box
            PopulateComboList();
            if (cmbFileTypes.Items.Count > 0)
            {
                cmbFileTypes.SelectedIndex = 0;
            }
        }

        private void PopulateComboList()
        {
            cmbFileTypes.Items.AddRange(FileTypes.GetFileTypes());
        }

        private void btnDest_Click(object sender, EventArgs e)
        {
            DialogResult dr = folderBrowserDialog.ShowDialog();

            if (dr == System.Windows.Forms.DialogResult.OK)
            {
                _destFolder = folderBrowserDialog.SelectedPath;
                txtDestFolder.Text = _destFolder;
            }
        }

        private void btnStartSearch_Click(object sender, EventArgs e)
        {
            toolStripStatusInfo.Visible = true;
            btnStartSearch.Enabled = false;
            fileListView.Items.Clear();
            fileListView.Refresh();
            _selectedFileType = cmbFileTypes.SelectedItem.ToString();

            List<Task> taskList = new List<Task>();
            DriveInfo[] drives = DriveInfo.GetDrives().Where(drive => drive.DriveType == DriveType.Fixed).ToArray();

            try
            {
                foreach (DriveInfo d in drives)
                {
                    DriveInfo dInfo = d;
                    //Task searchTask = Task.Factory.StartNew( () => { Fi}, _ct.Token, TaskCreationOptions.LongRunning, TaskScheduler.FromCurrentSynchronizationContext());
                    Task searchTask = Task.Factory.StartNew(() => { FindFiles(dInfo.RootDirectory, ref files); }
                                                                            , _ct.Token, TaskCreationOptions.LongRunning
                                                                            , TaskScheduler.FromCurrentSynchronizationContext());

                    System.Diagnostics.Trace.WriteLine("currently reading" + d.RootDirectory.FullName);
                    taskList.Add(searchTask);

                    searchTask.ContinueWith(PopulateResultList, TaskScheduler.FromCurrentSynchronizationContext());

                }

                Task.Factory.ContinueWhenAll(taskList.ToArray(), OnSearchCompleted, _ct.Token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext());

            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.WriteLine(ex.Message);
            }
            finally
            {

            }


        }

        private void OnSearchCompleted(Task[] tasks)
        {
            // hide notifier label
            toolStripStatusInfo.Visible = false;
            btnStartSearch.Enabled = true;
            btnZip.Enabled = true;
            toolStripStatusLabel.Text = string.Format("Total files found: {0}", files.Count);
        }

        private List<string> SearchFiles()
        {
            // DirectoryInfo dirSource = new DirectoryInfo(_sourceToSearch);
            _selectedFileType = cmbFileTypes.SelectedItem.ToString();
            return files;
        }

        private void FindFiles(DirectoryInfo directoryInfo, ref List<string> files)
        {
            FileSystemAccessRule rule = new FileSystemAccessRule(System.Security.Principal.WindowsIdentity.GetCurrent().Name, FileSystemRights.FullControl, System.Security.AccessControl.AccessControlType.Allow);

            try
            {
                foreach (DirectoryInfo d in directoryInfo.GetDirectories())
                {
                    //d.GetAccessControl().ResetAccessRule(rule);
                    System.Diagnostics.Trace.WriteLine("currently reading> " + d.FullName);
                    FindFiles(d, ref files);
                }

                files.AddRange(directoryInfo.GetFiles(string.Format("*.{0}", _selectedFileType), SearchOption.AllDirectories).Select(finfo => finfo.FullName));
            }
            catch (UnauthorizedAccessException excep)
            {
                System.Diagnostics.Trace.WriteLine(excep.Message);
            }
            catch (Exception e)
            {
                return; // MessageBox.Show(e.Message);
            }

        }

        private void PopulateResultList(Task searchedTask)
        {
            // fill up list view
            fileListView.Items.AddRange(files.Select(fileName => new ListViewItem { Checked = true, Text = fileName }).ToArray());
            toolStripStatusLabel.Text = string.Format("Total files found so far: {0}", files.Count);
        }

        private void btnZip_Click(object sender, EventArgs e)
        {

        }
    }
}

4 个答案:

答案 0 :(得分:1)

您正在使用当前任务计划程序启动任务,在这种情况下使用消息循环。

因此,当消息循环开始处理该任务时,它将阻塞直到完成。

尽量避免指定任务调度程序,它应该为您的任务启动新线程。

答案 1 :(得分:0)

您正在使用FindFiles函数执行一些非常繁重的recursive处理。如果你从目录结构的某个低位开始,你可能会看到一些缓慢的处理......

根据您的需要,您可能无法避免这种情况。您可以尝试在第一次拨打FindFiles之前和之后设置一个断点,看看这是否是问题区域。

答案 2 :(得分:0)

我怀疑您需要设置后台工作程序(或其他DoWork()线程方案)。这是来自无法估量的Jon Skeet的an article,可能有所帮助。

答案 3 :(得分:0)

将以下代码行放在任何具有密集处理的循环中

Application.DoEvents();

这可以避免使用多个线程并保持表单响应。但是,您必须小心禁用/重新启用您不希望用户在作业运行时单击的任何按钮。

线程通常被认为是处理无响应的更好方法,但它们更复杂,所以这是一个很好的替代方法。

如果您将DoEvents添加到FindFiles方法,则可以删除Factory.StartNew并直接为每个任务调用FindFiles。