2009-08-06 5 views
1

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

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 только через делегатов, и если да, для этого существуют особые соображения. Прошу прощения за длинную публикацию и благодарю вас за ваше время.

+0

Мое беспокойство заключается в том, что процедура doWork завершит работу (завершение работыComplete), пока ваш таймер все еще работает, вызывая OnTimerEvent. Вы уверены, что вам просто не нужен цикл с thread.Sleep в методе do do? – automatic

+0

Нет, я не могу. Логика, управляющая программным обеспечением, требует, чтобы я делал некоторую обработку каждую секунду. –

ответ

1

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

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

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

+0

Да, я что-то упустил? Из того, что я вижу в коде, таймер не может выйти из области действия (и, следовательно, не может быть собран): * он является членом родительского объекта (формы?) * прослушиватель событий подключен к Истекшее событие – jeroenh

+0

@ Джеруэн: Да, извините, что вы правы. (Будет редактировать). Я смотрел на то, что он был создан в фоновом рабочем методе. Если этот метод был запущен снова, новый таймер будет создан без очистки старого. –

+0

Спасибо всем за понимание. Мне жаль, что я не опубликовал весь код, я думал, что это слишком долго, поэтому я просто взял его части. Саймон: Я не могу избавиться от таймера, так как мой графический интерфейс сильно задержится, и в более ранней публикации было предложено, что я расскажу о задержке интерфейса o использовать фоновый рабочий. Я просто не был уверен, как идти о построении логики; создаю ли я, затем вызываю поток таймера в потоке фонового рабочего (что я пытаюсь сделать здесь) или создаю поток фонового рабочего потока в потоке таймера. –

1

Вам не нужно и 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 
} 

В button_click просто

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 и таймер, но я имел случаи, когда я использовали BackgroundWorker для выполнения работы при вызове таймера, чтобы периодически обновлять пользовательский интерфейс обновления таймера и иметь ручную кнопку для обновления пользовательского интерфейса. Но я не обновлял свой интерфейс так, как вы.

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

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