无法理解.NET中的后台工作者

时间:2010-04-09 12:57:02

标签: c# mono backgroundworker

我写了一个同步两个文件夹的应用程序。该程序的问题是它在复制文件时停止响应。快速搜索堆栈溢出告诉我,我需要使用一个称为后台工作程序的东西。我已经在网上阅读了几篇关于此的内容,但发现它很难理解,因为我对编程很新。我怎样才能简单地将所有File.Copy(....)命令放入他们自己的后台工作程序中(如果它甚至是如何工作的话)?下面是运行子过程的按钮单击事件的代码,以及我希望在所有File.Copy行上使用后台工作程序的子过程。

任何帮助将不胜感激,因为在此之后程序将完成:D

修改

根据以下Veer的建议,我相应地更改了我的代码,但是我不断遇到以下错误:

错误CS1061:键入Gtk.ProgressBar' does not contain a definition for InvokeRequired'并且找不到扩展方法InvokeRequired' of type Gtk.ProgressBar'(您是否缺少using指令或程序集引用?)(CS1061)(Sync-GUI) v2)的

错误CS1061:键入Gtk.ProgressBar' does not contain a definition for BeginInvoke'并且找不到扩展方法BeginInvoke' of type Gtk.ProgressBar'(您是否缺少using指令或程序集引用?)(CS1061)(Sync-GUI) v2)的

以下是我的代码。

按钮单击事件:

protected virtual void OnBtnSyncClicked (object sender, System.EventArgs e)
{
    //sets progress bar to 0
    prgProgressBar.Fraction = 0;

    //resets values used by progressbar
    dblCurrentStatus = 0;
    dblFolderSize = 0;

    //tests if user has entered the same folder for both target and destination
    if (fchFolder1.CurrentFolder == fchFolder2.CurrentFolder)
    {
        //creates message box
        MessageDialog msdSame = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, "You cannot sync two folders that are the same");
        //sets message box title
        msdSame.Title="Error";
        //sets respone type
        ResponseType response = (ResponseType) msdSame.Run();
        //if user clicks on close button or closes window then close message box
        if (response == ResponseType.Close || response == ResponseType.DeleteEvent) {
            msdSame.Destroy();
        }
        return;
    }

    //tests if user has entered a target folder that is an extension of the destination folder
    // or if user has entered a desatination folder that is an extension of the target folder
    if (fchFolder1.CurrentFolder.StartsWith(fchFolder2.CurrentFolder) || fchFolder2.CurrentFolder.StartsWith(fchFolder1.CurrentFolder))
    {
        //creates message box
        MessageDialog msdContains = new MessageDialog(this, DialogFlags.Modal, MessageType.Error, ButtonsType.Close, "You cannot sync a folder with one of its parent folders");
        //sets message box title
        msdContains.Title="Error";          
        //sets respone type and runs message box
        ResponseType response = (ResponseType) msdContains.Run();
        //if user clicks on close button or closes window then close message box
        if (response == ResponseType.Close || response == ResponseType.DeleteEvent)
        {
            msdContains.Destroy();
        }
        return;
    }   

    //creates background worker
    BackgroundWorker bwBackgroundWorker = new BackgroundWorker();
    bwBackgroundWorker.DoWork += new DoWorkEventHandler(bwBackgroundWorkerDoWorkFolder1);
    //starts background worker
    bwBackgroundWorker.RunWorkerAsync();

    //informs user process is complete
    prgProgressBar.Text = "Finished";
}

后台工作人员做工作事件:

private void bwBackgroundWorkerDoWorkFolder1 (object sender, DoWorkEventArgs e)
{
    //Gets total file size of folder 1
    TotalFileSizeFolder1(fchFolder1.CurrentFolder);
    //Syncs folder 1
    SyncFolder1(fchFolder1.CurrentFolder, fchFolder2.CurrentFolder);
}

TotalFileSizeFolder1子例程:

protected void TotalFileSizeFolder1 (string strCurrentDirectory)
{
    //inform user that file sizes are being gathered
    if (prgProgressBar.InvokeRequired)
    {
        prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Text="Getting total size of " + strCurrentDirectory;}));
    }

    //string array of all the directories in directory
    string[] staAllDirectories = Directory.GetDirectories(strCurrentDirectory);
    //string array of all the files in directory
    string[] staAllFiles = Directory.GetFiles(strCurrentDirectory);

    foreach (string strFile in staAllFiles)
    {
        //saves new file info called FileSize
        FileInfo FileSize = new FileInfo(strFile);
        //adds file size 
        dblFolderSize = dblFolderSize + FileSize.Length;
        //pulses progress bar to indicate some movement
        if (prgProgressBar.InvokeRequired)
    {
        prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Pulse();}));
    }

    }


    foreach (string strDirectory in staAllDirectories)
    {
        TotalFileSize(strDirectory);
    }
    //delete text from progress bar

    if (prgProgressBar.InvokeRequired)
    {
        prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Text="";}));
    }

}   

SyncFolder1 Sub Routine:

protected void SyncFolder1 (string strFolder1, string strFolder2)
{
    //string array of all the directories in directory
    string[] staAllDirectories = Directory.GetDirectories(strFolder1);
    //string array of all the files in directory
    string[] staAllFiles = Directory.GetFiles(strFolder1);

    //loop over each file in directory
    foreach (string strFile in staAllFiles)
    {
        //string of just the file's name and not its path
        string strFileName = System.IO.Path.GetFileName(strFile);
        //string containing directory in target folder
        string strDirectoryInsideFolder1 = System.IO.Path.GetDirectoryName(strFile).Substring(strFolder1.Length);

        //inform user as to what file is being copied
        if (prgProgressBar.InvokeRequired)
        {
            prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Text="Syncing " + strFile;}));
        }

        //tests if file does not exist in destination folder
        if (!File.Exists(fchFolder2.CurrentFolder + "/" + strDirectoryInsideFolder1 + "/" + strFileName))
        {
            //if file does not exist copy it to destination folder, the true below means overwrite if file already exists
            File.Copy (strFile, strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName, true);
        }

        //tests if file does exist in destination folder
        if (File.Exists(strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName))
        {
            //long (number) that contains date of last write time of target file
            long lngFolder1FileDate = File.GetLastWriteTime(strFile).ToFileTime();
            //long (number) that contains date of last write time of destination file
            long lngFolder2FileDate = File.GetLastWriteTime(strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName).ToFileTime();

            //tests if target file is newer than destination file
            if (lngFolder1FileDate > lngFolder2FileDate)
            {
                //if it is newer then copy file from target folder to destination folder
                File.Copy (strFile, strFolder2 + "/" + strDirectoryInsideFolder1 + "/" + strFileName, true);
            }   
        }
        //gets current file size
        FileInfo FileSize = new FileInfo(strFile);
        //sets file's filesize to dblCurrentStatus and adds it to current total of files 
        dblCurrentStatus = dblCurrentStatus + FileSize.Length;
        double dblPercentage = dblCurrentStatus/dblFolderSize;
        if (prgProgressBar.InvokeRequired)
        {
            prgProgressBar.BeginInvoke(new MethodInvoker(delegate {prgProgressBar.Fraction = dblPercentage;}));
        }
    }

    //loop over each folder in target folder
    foreach (string strDirectory in staAllDirectories)
    {
        //string containing directories inside target folder but not any higher directories
        string strDirectoryInsideFolder1 = strDirectory.Substring(strFolder1.Length);
        //tests if directory does not exist inside destination folder
        if (!Directory.Exists(strFolder2 + "/" + strDirectoryInsideFolder1))
        {
            //it directory does not exisit create it
            Directory.CreateDirectory(strFolder2 + "/" + strDirectoryInsideFolder1);
        }
        //run sync on all files in directory
        SyncFolders(strDirectory, strFolder2);
    }

}

2 个答案:

答案 0 :(得分:7)

初始化后台工作者对象

BackgroundWorker bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(bw_DoWork);

使用此代码

bw.RunWorkerAsync();  // Calls the bw_DoWork method

代替

//runs SyncTarget procedure      
SyncTarget(fchTarget.CurrentFolder);
//gets folder size of destination folder              
FileSizeOfDestination(fchDestination.CurrentFolder);              

定义DoWork方法

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
    SyncTarget(fchTarget.CurrentFolder);
    FileSizeOfDestination(fchDestination.CurrentFolder);
}

我不认为在这里使用两个后台工作程序是必要的,因为这两种方法都涉及IO操作。

您也可以使用RunWorkerCompleted和ProgressChanged

http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx

我还看到您在SyncTarget方法中访问UI元素。您无法从另一个线程访问您的UI元素。您必须使用BeginInvoke方法来完成此

if (prgProgressBar.InvokeRequired)
{
    prgProgressBar.BeginInvoke(new MethodInvoker(delegate { prgProgressBar.Text="Syncing " + strFile; }));
}

您也可以使用dispatcher实现此目的。

Dispatcher UIDispatcher = Dispatcher.CurrentDispatcher;  // Use this code in the UI thread

UIDispatcher.Invoke(DispatcherPriority.Normal, new Action(() =>  
{  
    // access your prgProgressBar here 
})); 

SO中有很多关于调度程序和跨线程操作的问题。你可以浏览它们。

我只使用prgProgressBar作为例子。但我建议你使用ProgressChanged方法中的进度条。

bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

private void bw_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
    prgProgressBar.Value = e.ProgressPercentage;
}

答案 1 :(得分:2)

创建BackgroundWorker对象,对于DoWork事件,输入要在后台运行的所有代码。然后,当您需要使用它时,请在对象上调用RunWorkerAsync()。