среда, 22 мая 2013 г.

Багатопотоковість в WPF. Використання класу BackgroundWorker.

Бувають ситуації, що в WPF апплікації потрібно виконувати ресурсоємні завдання, які споживають велику кількість часу. Виконання цих завдань в окремому потоці дозволяє підтримувати UI працездатним і доступним для введення даних .
BackgroundWorker є рекомендованим способом запуску трудомісткого завдання на окремому виділеному потоці, залишаючи UI працездатним.



За допомогою  BackgroundWorker  можна :
  • запустити фонову операцію
  • забезпечити параметри для фонової операції
  • повернути значення з фонової операції
  • відмінити фонову операцію
  • звітувати прогрес виконання фонової операції

 

Одні із найважливіших членів цього класу:
Проперті
CancellationPending Gets a value indicating whether the application has requested cancellation of a background operation.
IsBusy  Gets a value indicating whether the BackgroundWorker is running an asynchronous operation.
WorkerReportsProgress Gets or sets a value indicating whether the BackgroundWorker can report progress updates.
WorkerSupportsCancellation Gets or sets a value indicating whether the BackgroundWorker supports asynchronous cancellation.
Події
DoWork Occurs when RunWorkerAsync is called.
ProgressChanged Occurs when ReportProgress is called.
RunWorkerCompleted Occurs when the background operation has completed, has been canceled, or has raised an exception.
Методи
CancelAsync Requests cancellation of a pending background operation.
RunWorkerAsync() Starts execution of a background operation.

Запуск фонової операції
Метод RunWorkerAsync починає виконання фонового процесу рейзаючи подію DoWork. Код хендлера події DoWork виконується в окремому потоці.
        private void StartButton_OnClick(object sender, RoutedEventArgs e)
        {
            backgroundworker.RunWorkerAsync();
            
        }
        void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
        {
            //  perform some logic
        }
        public MainWindow()
        {
            InitializeComponent();
            backgroundworker.DoWork += backgroundworker_DoWork;
        }

 


Забезпечення параметра для фонової операції


Для фонової операції можливо потрібно буде один або декілька параметрів. Ви можете надати параметр в методі RunWorkerAsync , який буде доступні в якості проперті Argument  в хендлері події DoWork.
        private void StartButton_OnClick(object sender, RoutedEventArgs e)
        {
            backgroundworker.RunWorkerAsync(this.IntegerUpDown.Value);
             
        }
        void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (e.Argument != null && e.Argument is System.Int32)
            {
                int number = (int)e.Argument;
   
            }
        }

Відміна фонової операції


Можливо, ви захочете дозволити користувачеві скасувати довготривалу операцію. Щоб це було можливо, спочатку потрібно: 
        public MainWindow()
        {
            InitializeComponent();
            
            backgroundworker.WorkerSupportsCancellation = true;
            backgroundworker.DoWork += backgroundworker_DoWork;
        }

 
       #region Cancelling the Background Process

        private void CancelButton_OnClick(object sender, RoutedEventArgs e)
        {
            if (this.backgroundworker.IsBusy)
            {
                backgroundworker.CancelAsync();
            }
        }

        #endregion


  • перевіряти значення проперті CancellationPending в хендлері події DoWork Якщо вона приймає значення True, то  встановити пропертю Cancel параметра DoWorkEventArgs в True.


        void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (e.Argument != null && e.Argument is System.Int32)
            {
                int number = (int)e.Argument;

                if (number == 1) 
                {
                    e.Result = 1;
                    backgroundworker.ReportProgress(100);
                    return;
                }

                int factorial = 1;


                for (int i = 2; i < number+1; i++)
                {
                    if (backgroundworker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }
                    Thread.Sleep(500);
                    factorial = factorial * i;

                    backgroundworker.ReportProgress(i*100 / number);
                }

                e.Result = factorial;
            }
        }

Повернення значення з фонової операції


Можливо, ви захочете повернути значення з фонової операції, наприклад результат підрахунку. Для цього необхідно :

        void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (e.Argument != null && e.Argument is System.Int32)
            {
                int number = (int)e.Argument;
                if (number == 1) 
                {
                    e.Result = 1;
                    backgroundworker.ReportProgress(100);
                    return;
                }
                int factorial = 1;
                for (int i = 2; i < number+1; i++)
                {
                    if (backgroundworker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }
                    Thread.Sleep(500);
                    factorial = factorial * i;
                    backgroundworker.ReportProgress(i*100 / number);
                }
                e.Result = factorial;
            }
        }



        public MainWindow()
        {
            InitializeComponent();
            
            backgroundworker.WorkerSupportsCancellation = true;
            backgroundworker.DoWork += backgroundworker_DoWork;
            backgroundworker.RunWorkerCompleted += backgroundworker_RunWorkerCompleted;
        }

       #region Returning a Value from the Process

        void backgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Cancelled)
            {
                this.ShowResultTextblock.Text = "Operation was canceled !";
                this.Progressbar.Value = 0;
            }
            else
            {
                this.ShowResultTextblock.Text = String.Format("Factorial  is {0} ", e.Result.ToString() );
                this.Progressbar.Value = 0;
            }
            
        }

        #endregion
 

Звітування прогресу виконання фонової операції


Ви можете звітувати  про хід виконання фонової операції на основний потік  шляхом виклику методу ReportProgress. Цей метод викликає подію ProgressChanged і дозволяє передавати параметр, що вказує відсоток прогресу, який був завершений. Перед тим необхідно встановити пропертю WorkerReportsProgess класу BackgroundWorker в true.
        public MainWindow()
        {
            InitializeComponent();
            
            backgroundworker.WorkerSupportsCancellation = true;
            backgroundworker.WorkerReportsProgress = true;
            backgroundworker.DoWork += backgroundworker_DoWork;
            backgroundworker.RunWorkerCompleted += backgroundworker_RunWorkerCompleted;
            backgroundworker.ProgressChanged += backgroundworker_ProgressChanged;
        }
        void backgroundworker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (e.Argument != null && e.Argument is System.Int32)
            {
                int number = (int)e.Argument;

                if (number == 1) 
                {
                    e.Result = 1;
                    backgroundworker.ReportProgress(100);
                    return;
                }

                int factorial = 1;


                for (int i = 2; i < number+1; i++)
                {
                    if (backgroundworker.CancellationPending)
                    {
                        e.Cancel = true;
                        return;
                    }
                    Thread.Sleep(500);
                    factorial = factorial * i;

                    backgroundworker.ReportProgress(i*100 / number);
                }

                e.Result = factorial;
            }
        }
        #region Reporting the Progress of a Background Process

        void backgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            this.Progressbar.Value = e.ProgressPercentage;
        }

        #endregion

Скачати приклад

Комментариев нет:

Отправить комментарий