Поток BackgroundWorker и логика таймера - PullRequest
1 голос
/ 07 августа 2009

Я пытался настроить логику для моего таймера и фонового потока. Конечно, я не до конца понимаю всю систему, несмотря на все мои чтения. Ниже приведены выдержки из соответствующего кода: Моя кнопка опроса:

private void pollStart_Click(object sender, EventArgs e)
    {
        tst_bgw = new BackgroundWorker();
        //mandatory. Otherwise will throw an exception when calling ReportProgress method  
        tst_bgw.WorkerReportsProgress = true;
        //mandatory. Otherwise we would get an InvalidOperationException when trying to cancel the operation  
        tst_bgw.WorkerSupportsCancellation = true;
        tst_bgw.DoWork += tst_bgw_DoWork;
        tst_bgw.ProgressChanged += tst_bgw_ProgressChanged;
        tst_bgw.RunWorkerCompleted += tst_bgw_RunWorkerCompleted;
        tst_bgw.RunWorkerAsync();

    }

что я считаю правильным до сих пор

Мой фоновый рабочий поток:

private void tst_bgw_DoWork(object source, DoWorkEventArgs e)
    {
        m_timer = new System.Timers.Timer();
        m_timer.Interval = 1000;
        m_timer.Enabled = true;
        m_timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        if (tst_bgw.CancellationPending)
        {
            e.Cancel = true;
            return;
        }

    }

и код события прошедшего уровня:

private void OnTimedEvent(object source, ElapsedEventArgs e)
    {              
        if (powerVal > 3250)
        {
            m_timer.Stop();
            tst_bgw.CancelAsync();
        }
        else
        {
            string pow;                
            int progressVal = 100 - ((3250 - powerVal) / timerVal);
            uiDelegateTest tstDel = new uiDelegateTest(recvMessage);// the recvMessage function takes a textbox as an argument and directs output from socket to it.

            pow = construct_command("power", powerVal); 
            sData = Encoding.ASCII.GetBytes(pow);

            if (active_connection)
                try
                {
                    m_sock.Send(sData);
                    Array.Clear(sData, 0, sData.Length);
                    tstDel(ref unit_Output);// Read somewhere that you can only modify UI elements in this method via delegate so I think this is OK.
                    m_sock.Send(time_out_command);
                    tstDel(ref unit_Output);
                    tst_bgw.ReportProgress(progressVal);
                }
                catch (SocketException se)
                {
                    MessageBox.Show(se.Message);
                }
            tst_bgw.ReportProgress(powerVal, progressVal);
            powerVal = powerVal + pwrIncVal;
        }

Я просто хотел бы знать несколько других вещей; я использую правильный таймер (не то, чтобы я думал, что это должно иметь большое значение, но было предложено, что это может быть лучшим таймером для того, что я хочу сделать) и могу ли я действительно изменять элементы пользовательского интерфейса в методе DoWork только через делегатов, и если да, Есть особые соображения для этого. Извините за длинную публикацию и спасибо за ваше время.

Ответы [ 2 ]

1 голос
/ 07 августа 2009

Вам не нужны и BackgroundWorker, и Timer для достижения вашей цели. Из того, что вы опубликовали, похоже, что вы хотите, чтобы пользователь нажал кнопку, которая запускает процесс опроса, который завершается в определенной точке.

Ваша модель опроса действительно предполагает, что таймер будет работать нормально.

Если вы используете таймер, я бы инициализировал таймер после вызова InitializeComponent () чем-то вроде

private void InitializeTimer()
{
    this.timer = new Timer();
    int seconds = 1;
    this.timer.Interval = 1000 * seconds; // 1000 * n where n == seconds
    this.timer.Tick += new EventHandler(timer_Tick);
    // don't start timer until user clicks Start
}

Нажатие кнопки просто

private void button_Click(object sender, EventArgs e)
{
    this.timer.Start();
}

Затем на отметке timer_Tick вам нужно будет выполнить опрос, и вы сможете обновить свой пользовательский интерфейс оттуда, если таймер находится в потоке пользовательского интерфейса, как это

void timer_Tick(object sender, EventArgs e)
{
   if( determineIfTimerShouldStop() )
   {
       this.timer.Stop();
   }
   else
   {
       // write a method to just get the power value from your socket
       int powerValue = getPowerValue();

       // set progressbar, label, etc with value from method above
   }
}

Однако, если поток таймера не находится в том же потоке, что и пользовательский интерфейс, вы получите исключение при попытке обновить пользовательский интерфейс. В этом случае вы можете использовать Invoke, о котором упоминает DataDink , и сделать что-то подобное

void timer_Tick(object sender, EventArgs e)
{
   if( determineIfTimerShouldStop() )
   {
       this.timer.Stop();
   }
   else
   {
       // write a method to just get the power value from your socket
       int powerValue = getPowerValue();

       // set a label with Invoke
        mylabel.Invoke( 
            new MethodInvoker( delegate { mylabel.Text = "some string"; } )
                      );
   }
}

Учитывая код, который вы разместили, вам действительно не нужно было использовать и BackgroundWorker, и Timer, но у меня были случаи, когда я использовал BackgroundWorker для выполнения работы при вызове таймера, чтобы я мог обновить его Пользовательский интерфейс периодически и ручная кнопка для обновления пользовательского интерфейса. Но я обновлял свой интерфейс не так, как вы.

Если у вас все еще есть необходимость сделать и то и другое, вот, примерно, как вы можете запустить свое приложение ...

  • Создать Метод InitailizeBackgroundWorker () вместе с InitializeTimer, так что у вас есть это уже инициализировано до Таймер срабатывает.
  • Затем установите Timer.Tick позвонить BackgroundWorker.RunWorkerAsync ()
  • Затем вы можете делать все обновления пользовательского интерфейса из RunWorkerAsync: с использованием BackgroundWorker.ReportProgress ().
1 голос
/ 07 августа 2009

С этим кодом много ошибок.

1) Вы не избавляетесь от своего второстепенного работника. BackgroundWorkers должны быть утилизированы после использования. Они предназначены для использования в качестве компонентов winforms и обычно добавляются в окно через конструктор. Это гарантирует, что он будет создан с формой и удалится, когда форма.
2) Все, что вы делаете в методе dowork, - это создаете новый таймер и запускаете его. Нет смысла делать это в фоновом режиме, потому что в любом случае это произойдет так быстро.
3) Вы будете воссоздавать таймер каждый раз, когда снова запускаете фоновый рабочий. Но вы никогда не останавливаетесь и не утилизируете старый таймер, вы просто перезаписываете элемент.

Я рекомендую вам полностью избавиться от BackgroundWorker и просто использовать таймер. Создайте таймер в конструкторе форм и убедитесь, что вы используете его в методе удаления форм. (Или используйте дизайнер, чтобы добавить его в форму). В методе pollstart_click просто запустите таймер. (Если у вас есть метод остановки опроса, вы можете остановить таймер в этом)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...