Не могу разобраться с фоновыми работниками в .NET - PullRequest
3 голосов
/ 09 апреля 2010

Я написал приложение, которое синхронизирует две папки вместе. Проблема с программой заключается в том, что она перестает отвечать при копировании файлов. Быстрый поиск переполнения стека показал мне, что мне нужно использовать то, что называется фоновым рабочим. Я прочитал об этом несколько страниц в сети, но мне очень трудно это понять, так как я довольно новичок в программировании. Как я могу просто поместить все команды File.Copy (....) в их собственный фоновый рабочий (если это даже так, как это работает)? Ниже приведен код события нажатия кнопки, запускающего подпроцедуру, и подпроцедуру, которую я хочу использовать в качестве рабочего фона для всех строк File.Copy.

Любая помощь будет принята с благодарностью, так как после этого программа будет в значительной степени завершена: D

EDIT

Следуя совету Вира, я изменил свой код соответствующим образом, однако продолжаю получать следующие ошибки:

Ошибка 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:

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 ]

7 голосов
/ 09 апреля 2010

Инициализируйте ваш фоновый рабочий объект

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

Я не думаю, что использование двух фоновых рабочих здесь необходимо, поскольку оба метода участвуют в операциях ввода-вывода.

Вы также можете использовать RunWorkerCompleted и ProgressChanged

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

И я также вижу, что вы получаете доступ к элементам пользовательского интерфейса в вашем методе SyncTarget. Вы не можете получить доступ к вашему элементу пользовательского интерфейса из другого потока. Вы должны использовать метод 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;
}
2 голосов
/ 09 апреля 2010

Создайте объект BackgroundWorker и для события DoWork вставьте весь код, который вы хотите запустить в фоновом режиме. Затем, когда вам нужно его использовать, вызовите RunWorkerAsync () для объекта.

...