С помощью таймера с Backgroundworker для обеспечения метода dowork называется


У меня есть Windows Forms в которой backgroundworker вызывается снова и снова. Мне нужно, чтобы избежать одновременного доступа код в метод dowork для backgroundWorker, но также должны гарантировать, что код в метод dowork называется; поэтому я не могу просто избежать выполнения backgroundworker, если он занят.

Код приведен ниже, с подробными комментариями: Код как это работает красиво; просьба оценить код и таким образом на достижение намеченного; пожалуйста, дайте мне знать о любых потенциальных проблем,кода пахнет, дизайн недостатки или лучший способ сделать это. Какой-либо комментариях, ответы будут большим подспорьем.

using System;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;

namespace WindowsFormsApplication1
 {
  public partial class Form1 : Form
   {
    // this timer calls bgWorker again and again after regular intervals
    System.Windows.Forms.Timer tmrCallBgWorker;

    // this is our worker
    BackgroundWorker bgWorker;

    // this is the timer to make sure that worker gets called
    System.Threading.Timer tmrEnsureWorkerGetsCalled;

    // object used for safe access
    object lockObject = new object();

    public Form1()
    {
        InitializeComponent();
        // this timer calls bgWorker again and again after regular intervals
        tmrCallBgWorker = new System.Windows.Forms.Timer();
        tmrCallBgWorker.Tick += new EventHandler(tmrCallBgWorker_Tick);
        tmrCallBgWorker.Interval = 100;

        // this is our worker
        bgWorker = new BackgroundWorker();
        // work happens in this method
        bgWorker.DoWork += new DoWorkEventHandler(bg_DoWork);
        bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bg_RunWorkerCompleted);

    }

    void bg_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Complete");
    }

    void bg_DoWork(object sender, DoWorkEventArgs e)
    {
        // does a job like writing to serial communication, webservices etc
        System.Threading.Thread.Sleep(100);
    }

    void tmrCallBgWorker_Tick(object sender, EventArgs e)
    {
        if (Monitor.TryEnter(lockObject))
        {
            try
            {
                // if bgworker is not busy the call the worker
                if (!bgWorker.IsBusy)
                    bgWorker.RunWorkerAsync();
            }
            finally
            {
                Monitor.Exit(lockObject);
            }

        }
        else
        {

            // as the bgworker is busy we will start a timer that will try to call the bgworker again after some time
            tmrEnsureWorkerGetsCalled = new System.Threading.Timer(new TimerCallback(tmrEnsureWorkerGetsCalled_Callback), null, 0, 10);

        }

    }


    void tmrEnsureWorkerGetsCalled_Callback(object obj)
    {
        // this timer was started as the bgworker was busy before now it will try to call the bgworker again
        if (Monitor.TryEnter(lockObject))
        {
            try
            {
                if (!bgWorker.IsBusy)
                    bgWorker.RunWorkerAsync();
            }
            finally
            {
                Monitor.Exit(lockObject);
            }
            tmrEnsureWorkerGetsCalled = null;
        }
    }

    private void button1_Click(object sender, EventArgs e)
    {
        tmrCallBgWorker.Start();
    }
  }
}


20534
6
задан 9 августа 2011 в 02:08 Источник Поделиться
Комментарии
2 ответа

Мне нужно что-то подобное, в этом я имел код, который собирался периодически проверять базу данных для новой записи и уведомлять пользователя с шаре советы. Проверка базы данных каждые n-секунд вызвал паузу в пользовательском интерфейсе, что я не хотите, чтобы пользователь опыт, поэтому я перенес процедуру на поток.

У меня была система.Окна.Форм.События Tick таймера, где-нить было создано что-то вроде (оставив все обработки исключений и т. д.):

TIMER_TICK(object sender, EventArgs){
Thread checkNow=new Thread(ProcessAutoCheck)
checkNow.IsBackground=true;
checkNow.Start();
checkNow.Join(500);
}

Затем в методе ProcessAutoCheck, я использовал подход блокировки для предотвращения конкуренции потока:

**lock**(lockObject){
{
....

Программы работали как задумано, и я не потреблять ресурсы с потоками. Это не было модным решением. Я многому научился у многопоточность в C# - от Joseph Albahari. Полная книга онлайн.

5
ответ дан 4 февраля 2012 в 05:02 Источник Поделиться

Если вы не можете войти в замок, это означает, что "работа-очереди" становится резервной копии, верно? Это означает, что работа не выполняется достаточно быстро, так что вполне возможно, что tmrEnsureWorkerGetsCalled может получить перезаписаны, если tmrCallBgWorker пожаров в два раза, а один длинный-выполнение задания, предотвращает замок от. Перезаписанный таймер будет жить, пока сборщик мусора очищает его, и будут потеряны. Поэтому он не выполнил свою работу, чтобы гарантировать, что его делегат выполняется.

Если вам действительно нужно, чтобы убедиться, что вы стреляете в этот код запускается на выполнение, почему бы не использовать очередь или коллекции concurrentqueue, а не создание неявного очередь через использование таймеров?

2
ответ дан 9 августа 2011 в 03:08 Источник Поделиться