C# Ускорить Работу Backgroundworkerа

Тема в разделе ".NET", создана пользователем Villiya, 7 янв 2012.

  1. Villiya

    Villiya Гость

    есть 3 строчки кода которые необходимо выполнить
    Код (Text):
     /////данный кусок должен остаться неизменным
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    ////
    они являются очень временнозатратными т.е. в самом лучшем случае они расчитываются минут 5, чтобы не висла на это время форма, вынесла их в backgroundWorker следующим образом
    Код (Text):
    private void CalcBtn_Click(object sender, EventArgs e)
    {
    backgroundWorker1.RunWorkerAsync();

    }

    public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
    for (int i = 0; i < 100; i++)
    {

    /////данный кусок должен остаться неизменным
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    ////


    backgroundWorker1.ReportProgress(i);
    }        
    }

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

    public void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
    MessageBox.Show("Работа завершена");
    }
    процесс вроде выполняется, но ОООООЧЕНЬ медленно, может есть возможность его ускорить? может, например, лучше использовать
    Код (Text):
    var thread = new Thread();
    thread.Start();
    но как тогда в новом потоке правильно запустить
    Код (Text):
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    и прикрутить к нему прогрессбар?

    не отправляйте пожалуйста к литературе, уже было много перечитано, но что-то ничего не помогает
     
  2. LuMee

    LuMee Well-Known Member

    Регистрация:
    2 май 2006
    Сообщения:
    477
    Симпатии:
    0
    "Ручное" использование Thread вряд ли поможет, ибо BackgroundWorker являет собой, по сути, простую обертку над этим самым Thread'ом.
    Полагаю, самым правильным будет оптимизировать сам метод CFirDoc.Go, раз уж он так медленно работает. Рекомендую для начала натравить на него какой нибудь профайлер (я лично предпочитаю EQUATEC, но любой сгодится), чтобы сразу выявить узкие места.
    Также интересно, зачем нужен этот фрагмент кода:
    Код (Text):
    Form1.cdoc = p
    Если его убрать, то можно просто запустить 100 потоков (через ThreadPool, чтобы красиво), каждый из которых будет выполнять CFirDoc.Go для своего id. На многоядерной/многопроцессорной машине это может дать выигрыш в производительности.
     
  3. Villiya

    Villiya Гость

    Код (Text):
    Form1.cdoc = p
    используется для передачи данных через экземпляр класса.

    нашла вот такое замечание

    Использование ThreadPool для длительных вычислений крайне не рекомендуется, потому что количество потоков в пуле ограничено и создаются они небыстро. Поэтому запуск длительных вычислений в ThreadPool может привести к ухудшению производительности.

    но если же его все-таки использовать, то каким образом будет выглядеть код? что типа такого?
    Код (Text):
    private void CalcBtn_Click(object sender, EventArgs e)
    {
    ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));

    }
    static void ThreadProc(Object stateInfo)
    {
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    }
    но как тогда в этом случае отобразить ход вычислений на прогрессбаре? и вообще сообщить пользователю что вычисления закончены?
     
  4. Villiya

    Villiya Гость

    кажется я понимаю почему процесс вычислений так затягивается - я выполняю в цикле один и тот же код 100 раз
    Код (Text):
    public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
    for (int i = 0; i < 100; i++)
    {
    /////данный кусок должен остаться неизменным
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    ////
    backgroundWorker1.ReportProgress(i);
    }        
    }
    но если записать например так
    Код (Text):
    public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
    for (int i = 0; i < 100; i++)
    {
    backgroundWorker1.ReportProgress(i);
    }
    /////данный кусок должен остаться неизменным
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    ////         
    }
    то прогрессбар быстро заполняется и стоит на месте, в то время как вычмсления проходят сами по себе, тогда такой вопрос: куда правильно вставить код
    Код (Text):
    /////данный кусок должен остаться неизменным
    CFirDoc p = new CFirDoc(id);             
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    ////
    чтобы и он выполнялся нормально в новом потоке и при этом прогрессбар отображал реальный ход вычислений?
     
  5. LuMee

    LuMee Well-Known Member

    Регистрация:
    2 май 2006
    Сообщения:
    477
    Симпатии:
    0
    Ну если метод Go должен вызываться только один раз, то ситуация в корне меняется: работа по оповещению об изменении прогресса целиком и полностью ложиться на хрупкие плечи CFirDoc. Сделать это можно по аналогии с BackgroundWorker:
    1. В классе CFirDoc создаем событие, которое будет вызываться при изменении прогресса:
    Код (Text):
    public class ProgressEventArgs : EventArgs
    {
    public int Progress { get; private set; }

    public ProgressEventArgs(int progress)
    {
    this.Progress = progress;
    }
    }

    public class CFirDoc
    {
    public event EventHandler<ProgressEventArgs> Progress;

    private void OnProgress(int progress)
    {
    var handler = this.Progress;
    if (handler != null)
    handler.Invoke(new ProgressEventArgs(progress));
    }

    // Остальной код класса
    ...
    }
    2. В теле метода Go в нужных местах втыкаем вызовы OnProgress с указанием текущего прогресса:
    Код (Text):
    public double Go()
    {
    // Что-то делаем
    ...

    // Сделано примерно 15% работы
    OnProgress(15);

    // Еще что-то делаем
    ...

    // Сделано примерно 60% работы
    OnProgress(60);

    // Доделываем
    ...

    // Доделали
    OnProgress(100);
    }
    3. Перед вызовом метода Go назначаем обработчик события:
    Код (Text):
    CFirDoc p = new CFirDoc(id);
    p.Progress += delegate (object sender, ProgressEventArgs pea)
    {
    // Отображаем текущий прогресс
    };
    Без BackgroundWorker'а в этом случае можно, в принципе, и обойтись, используя обычный Thread.
     
  6. Villiya

    Villiya Гость

    т.е. достаточно написать в обработчик события на кнопке как-то так?
    Код (Text):
     Thread tre = new Thread(new ThreadStart(play));
    tre.Start();
    tre.Join();
    tre = null;
    где
    Код (Text):
    public void play()
    {
    /////данный кусок должен остаться неизменным
    CFirDoc p = new CFirDoc(id);
    p.Progress += delegate (object sender, ProgressEventArgs pea)
    double TotalSeconds = p.Go();
    Form1.cdoc = p;
    }
    и еще маленький глупый вопрос:
    Код (Text):
    {
    // Отображаем текущий прогресс
    };
    как это правильно будет записать? в случае использования Thread? и если все-таки использовать backgroundWorker1, то это будет выглядеть так?
    Код (Text):
    backgroundWorker1.ReportProgress(i);
     
  7. Villiya

    Villiya Гость

    да и наверно еще один вопрос, думаю не менее глупый: в классе Form1 пишу следующее
    Код (Text):
    private void CalcBtn_Click(object sender, EventArgs e)
    {
    Thread NewThread = new Thread(new ThreadStart(RunThread));
    NewThread.IsBackground = true;         
    // Start the new thread.
    NewThread.Start();       
    }


    public void RunThread()
    {
    CFirDoc p = new CFirDoc(id);
    p.Progress += delegate (object sender, ProgressEventArgs pea)
    {
    //какое-то отбражение
    }
    // запуск расчета на выполнение (возвращает затраченное время в секундах)
    double TotalSeconds = p.Go();        
    Form1.cdoc = p;
    }
    и вроде все нормально, а вот класс CFirDoc - это самостоятельно существующий,отдельный от Form1 класс и если я прописываю
    Код (Text):
     namespace ParallelCs
    {
    public class ProgressEventArgs : EventArgs
    {
    public int Progress { get; private set; }

    public ProgressEventArgs(int progress)
    {
    this.Progress = progress;
    }
    }

    [Serializable]
    public class CFirDoc
    {
    public event EventHandler<ProgressEventArgs> Progress;

    private void OnProgress(int progress)
    {
    var handler = this.Progress;
    if (handler != null)
    handler.Invoke(new ProgressEventArgs(progress));
    }
    //еще какой-то код
    }
    }
    то программа ругается на строчку
    Код (Text):
    handler.Invoke(new ProgressEventArgs(progress));
    и выдается следующая ошибка
    Код (Text):
    Delegate 'EventHandler' does not take 1 arguments
    в чем проблема?
     
  8. LuMee

    LuMee Well-Known Member

    Регистрация:
    2 май 2006
    Сообщения:
    477
    Симпатии:
    0
    Виноват, тут должно быть:
    Код (Text):
    handler.Invoke(this, new ProgressEventArgs(progress));
    EventHandler должен принимать два аргумента: объект, инициировавший событие, и аргументы события.

    BackgroundWorker тут точно не нужен (зачем плодить лишние сущности?). Делается простой обработчик, устанавливающий текущее значение прогресс-бара, только не надо забывать про InvokeRequired/Invoke:
    Код (Text):
    private void UpdateProgress(object sender, ProgressEventArgs pea)
    {
    if (progressBar.InvokeRequired)
    progressBar.Invoke(new EventHandler<ProgressEventArgs>(this.Progess), sender, e);
    else
    progressBar.Value = pea.Progress;
    }

    ...

    var p = new CFirDoc();
    p.Progress += this.UpdateProgress;
    ...
     
  9. Villiya

    Villiya Гость

    итого в классе CFirDoc имеется следующий код:
    Код (Text):
    namespace ParallelCs
    {  
    public class ProgressEventArgs : EventArgs
    {
    public int Progress { get; private set; }

    public ProgressEventArgs(int progress)
    {
    this.Progress = progress;
    }
    }

    [Serializable]
    public class CFirDoc
    {
    public event EventHandler<ProgressEventArgs> Progress;

    private void OnProgress(int progress)
    {
    var handler = this.Progress;
    if (handler != null)
    handler.Invoke(this, new ProgressEventArgs(progress));
    }
    //еще какой-то код
    }
    при этом на Form1 имеется следующее:
    Код (Text):
    private void UpdateProgress(object sender, ProgressEventArgs pea)
    {
    if (progressBar1.InvokeRequired)
    progressBar1.Invoke(new EventHandler<ProgressEventArgs>(this.Progress), sender, e);
    else
    progressBar1.Value = pea.Progress;
    }
    //расчет
    private void CalcBtn_Click(object sender, EventArgs e)
    {          
    Thread NewThread = new Thread(new ThreadStart(RunThread));
    NewThread.IsBackground = true;         
    // Start the new thread.
    NewThread.Start();         
    }

    public void RunThread()
    {
    CFirDoc p = new CFirDoc(id);
    p.Progress += delegate(object sender, ProgressEventArgs pea)
    {
    p.Progress += this.UpdateProgress;
    };
    // запуск расчета на выполнение (возвращает затраченное время в секундах)
    double TotalSeconds = p.Go();        
    Form1.cdoc = p;
    }
    теперь проблема в
    Код (Text):
     progressBar1.Invoke(new EventHandler<ProgressEventArgs>(this.Progress), sender, e);
    выдаются ошибки
    Код (Text):
    1.'ParallelCs.Form1' does not contain a definition for 'Progress' and no extension method 'Progress' accepting a first argument of type 'DispersionParallelCs.Form1' could be found (are you missing a using directive or an assembly reference?)
    2.The name 'e' does not exist in the current context
    я что-то делаю не так?
     
  10. LuMee

    LuMee Well-Known Member

    Регистрация:
    2 май 2006
    Сообщения:
    477
    Симпатии:
    0
    В проблемной строчке надо this.Progress заменить на this.UpdateProgress. Очепятался.
     
  11. Villiya

    Villiya Гость

    что-то опять ошибки...все в той же строчке
    Код (Text):
     progressBar1.Invoke(new EventHandler<ProgressEventArgs>(this.UpdateProgress), sender, e);
    пишет что е не существует в данном контексте, может там не е, а что-то другое должно быть? может реа?
     
  12. Villiya

    Villiya Гость

    исправила как писала выше - все заработало! :lol: ОГРОМНОЕ спасибо LuMee за потраченное время, силы и проявленное терпение!!!!!!!!очень помог! спасибо еще раз!!!
     
Загрузка...

Поделиться этой страницей