none
WPF - Как правильно использовать Task, Timer и BackgroundWorker совместно ? RRS feed

  • Вопрос

  • Привет всем. Совсем недавно начал изучать работу с потоками в WPF и возникли немного вопросов, на которые, надеюсь, смогу ответить с вашей помощью. Задача программы выполнить определенную работу каждые 10 секунд. Разбить эту работу на несколько частей используя потоки (Task) и при этом использовать BackgroundWorker чтобы пользовательский интерфейс не зависал во время выполнения работ. В интернете нашел много пример работы с тасками, но не смог найти несложные примеры, которые бы обьяснили как таски правильно использовать с таймером, BackgroundWorker и локами конечно. Для ясности привожу пример, которую написал, чтобы вы могли посмотреть и посоветовать правильно ли написан код. Благодаря вашим советам я смогу лучше понять как работает многопоточность в WPF. Жду с нетерпением ваших сообщений. Спасибо.

    namespace timer_and_thread
        {
            /// <summary>
            /// Interaction logic for MainWindow.xaml
            /// </summary>
            public partial class MainWindow : Window
            {
                DispatcherTimer TimerObject;
                Task[] tasks;
                readonly object _countLock = new object();
                int[] Summ = new int[10];
                int Index = 0;
     
                public MainWindow()
                {
                    InitializeComponent();
                    TimerObject = new DispatcherTimer();
                    TimerObject.Tick += new EventHandler(timer_Elapsed);
                    TimerObject.Interval = new TimeSpan(0, 0, 10);
                }
     
                // вызвать метод каждые 10 секунд
                private void timer_Elapsed(object sender, EventArgs e)
                {
                    TimerObject.Stop();
     
                    BackgroundWorker backgroundWorkerObject = new BackgroundWorker();
                    backgroundWorkerObject.DoWork += new DoWorkEventHandler(StartThreads);
                    backgroundWorkerObject.RunWorkerAsync();
     
                    TimerObject.Start();
                }
     
     
                private void StartThreads(object sender, DoWorkEventArgs e)
                {
                    tasks = new Task[4];
                    tasks[0] = Task.Factory.StartNew(() => DoSomeLongWork());
                    tasks[1] = Task.Factory.StartNew(() => DoSomeLongWork());
                    tasks[2] = Task.Factory.StartNew(() => DoSomeLongWork());
                    tasks[3] = Task.Factory.StartNew(() => DoSomeLongWork());
     
                    // Даем задачам 1 секунду для их запуска 
                    Thread.Sleep(1000);
                }
     
                private void DoSomeLongWork()
                {
                    while (Index < Summ.Length)
                    {
                        int localIndex = 0;
     
                        // блокируем глобальную переменную Index 
                        lock (_countLock)
                        {
                            localIndex = Index;
                            Index++;
                        }
     
                        // Я написал простой пример работы. Тут может быть большая вычислительная работа.
                        Random rnd = new Random();
                        int someResult = rnd.Next(1, 100000);
     
                        //  блокируем глобальную переменную Summ для записи в нее вычисленного результата
                        lock (_countLock)
                        {
                            Summ[localIndex] = someResult;
                        }      
                    }
                }
     
                // Кнопка, с помощью которой запускаю таймер и начинается работа приложения
                private void Start_Button_Click_1(object sender, RoutedEventArgs e)
                {
                    TimerObject.Start();
                }
     
            }
        }
    30 августа 2013 г. 13:04

Ответы

  • Если Вам не требуется взаимодействие с UI, то можно обойтись без BackgroundWorker. Да и вместо ручного разделения задачи на части логичнее воспользоваться встроенным классом Parallel который предлагает параллельные варианты циклов for и foreach.
    private void timer_Elapsed(object sender, EventArgs e)
    {
        TimerObject.Stop();
    
        Task.Factory.StartNew(BackgroundWork);
    
        TimerObject.Start();
    }
    
    private void BackgroundWork()
    {
        Parallel.For(0,Summ.Length,new ParallelOptions{MaxDegreeOfParallelism=4},DoSomeWork);
    }
    
    private void DoSomeWork(int localIndex)
    {
        Random rnd = new Random();
        int someResult = rnd.Next(1, 100000);
    
        Summ[localIndex] = someResult;
    }
    1 сентября 2013 г. 9:23

Все ответы

  • Под какую версию .NET Framework пишите? Какой функционал от фоновой задачи Вам нужен? Уведомление UI о прогрессе в процессе выполнения задачи? Обновление UI после выполнения задачи? Только получение результата?
    1 сентября 2013 г. 4:39
  • .NET Framework 4.0 .  От фоновой задачи не нужно ничего получать. Похоже получает она вовсе и не нужна, то есть можно обойтись без фоновой задачи, так?
    1 сентября 2013 г. 8:51
  • Если Вам не требуется взаимодействие с UI, то можно обойтись без BackgroundWorker. Да и вместо ручного разделения задачи на части логичнее воспользоваться встроенным классом Parallel который предлагает параллельные варианты циклов for и foreach.
    private void timer_Elapsed(object sender, EventArgs e)
    {
        TimerObject.Stop();
    
        Task.Factory.StartNew(BackgroundWork);
    
        TimerObject.Start();
    }
    
    private void BackgroundWork()
    {
        Parallel.For(0,Summ.Length,new ParallelOptions{MaxDegreeOfParallelism=4},DoSomeWork);
    }
    
    private void DoSomeWork(int localIndex)
    {
        Random rnd = new Random();
        int someResult = rnd.Next(1, 100000);
    
        Summ[localIndex] = someResult;
    }
    1 сентября 2013 г. 9:23