简体   繁体   English

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

[英]Can't get my head around background workers in .NET

I have wrote an application that syncs two folders together. 我写了一个同步两个文件夹的应用程序。 The problem with the program is that it stops responding whilst copying files. 该程序的问题是它在复制文件时停止响应。 A quick search of stack-overflow told me I need to use something called a background worker. 快速搜索堆栈溢出告诉我,我需要使用一个称为后台工作程序的东西。 I have read a few pages on the net about this but find it really hard to understand as I'm pretty new to programming. 我已经在网上阅读了几篇关于此的内容,但发现它很难理解,因为我对编程很新。 How can I simply put all of the File.Copy(....) commands into their own background worker (if that's even how it works)? 我怎样才能简单地将所有File.Copy(....)命令放入他们自己的后台工作程序中(如果它甚至是如何工作的话)? Below is the code for the button click event that runs the sub procedure and the sub procedure I wish to use a background worker on all the File.Copy lines. 下面是运行子过程的按钮单击事件的代码,以及我希望在所有File.Copy行上使用后台工作程序的子过程。

Any help will be greatly appreciated as after this the program will pretty much be finished :D 任何帮助将不胜感激,因为在此之后程序将完成:D

EDIT 编辑

Using the advice of Veer below I have changed my code accordingly however I keep getting the follwoing errors: 使用Veer的建议我已经相应地更改了我的代码,但是我不断遇到以下错误:

Error CS1061: Type Gtk.ProgressBar' does not contain a definition for InvokeRequired' and no extension method InvokeRequired' of type Gtk.ProgressBar' could be found (are you missing a using directive or an assembly reference?) (CS1061) (Sync-GUI v2) 错误CS1061:类型Gtk.ProgressBar' does not contain a definition for并且找不到InvokeRequired' of type Gtk.ProgressBar' does not contain a definition for Gtk.ProgressBar的扩展方法InvokeRequired' of type (您是否缺少using指令或程序集引用?)(CS1061)(同步 - GUI v2)

Error CS1061: Type Gtk.ProgressBar' does not contain a definition for BeginInvoke' and no extension method BeginInvoke' of type Gtk.ProgressBar' could be found (are you missing a using directive or an assembly reference?) (CS1061) (Sync-GUI v2) 错误CS1061:类型Gtk.ProgressBar' does not contain a definition for BeginInvoke' Gtk.ProgressBar' does not contain a definition for并且找不到BeginInvoke' of type Gtk.ProgressBar'的扩展方法BeginInvoke' of type (您是否缺少using指令或程序集引用?)(CS1061)(同步 - GUI v2)

Below is my code. 以下是我的代码。

Button Click Event: 按钮单击事件:

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";
}

Background Worker Do Work Event: 后台工作人员做工作事件:

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 Sub Routine: 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: SyncFolder1子例程:

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);
    }

}

Initialise your background worker object 初始化后台工作程序对象

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

Use this code 使用此代码

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

in the place of 代替

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

Define the DoWork method 定义DoWork方法

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

I don't think using two background workers here is necessary because both the methods are involved in IO operations. 我不认为在这里使用两个后台工作程序是必要的,因为这两种方法都涉及IO操作。

You can also well make use of RunWorkerCompleted and ProgressChanged 您还可以使用RunWorkerCompleted和ProgressChanged

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

And I also see that you access UI elements in your SyncTarget method. 我还看到您在SyncTarget方法中访问UI元素。 You cannot access your UI element from another thread. 您无法从另一个线程访问您的UI元素。 You have to use the BeginInvoke method to accomplish this 您必须使用BeginInvoke方法来完成此任务

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

You can also achieve this using dispatcher . 您也可以使用调度程序实现此目的。

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

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

There are many question in SO on dispatcher and Cross threading operations. 在调度员和跨线程操作中有很多问题。 You can browse them out. 你可以浏览它们。

I used prgProgressBar just for an example. 我只使用prgProgressBar作为例子。 But i would recommend you to use the progress bar in the ProgressChanged method. 但我建议您使用ProgressChanged方法中的进度条。

bw.ProgressChanged += new ProgressChangedEventHandler(bw_ProgressChanged);

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

Create a BackgroundWorker object, and for the DoWork event, put in all the code you want to run in the background. 创建BackgroundWorker对象,并为DoWork事件添加要在后台运行的所有代码。 Then when you need to use it, call RunWorkerAsync() on the object. 然后,当您需要使用它时,在对象上调用RunWorkerAsync()。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM