简体   繁体   English

如何使用带有计时器刻度的BackgroundWorker?

[英]How can i use a BackgroundWorker with a timer tick?

Decided to not use any timers. 决定不使用任何计时器。 What i did is simpler. 我所做的比较简单。

Added a backgroundworker. 添加了背景工。 Added a Shown event the Shown event fire after all the constructor have been loaded. 添加了一个Shown事件,该Shown事件在所有构造函数加载后触发。 In the Shown event im starting the backgroundworker async. 在显示的事件中,im启动backgroundworker异步。

In the backgroundworker DoWork im doing: 在后台工作人员DoWork中,我正在执行以下操作:

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
        {
            while(true)
            {
                cpuView();
                gpuView();
                Thread.Sleep(1000);
            }
        }

In this case it's better to use two System.Threading.Timer and execute your cpu-intensive operations in these two threads. 在这种情况下,最好使用两个System.Threading.Timer并在这两个线程中执行CPU密集型操作。 Please note that you must access controls with BeginInvoke . 请注意,您必须使用BeginInvoke访问控件。 You can encapsulate those accesses into properties setter or even better pull them out to a view model class. 您可以将这些访问封装到属性设置器中,甚至更好地将它们拉到视图模型类中。

public class MyForm : Form
{
    private System.Threading.Timer gpuUpdateTimer;
    private System.Threading.Timer cpuUpdateTimer;

    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);

        if (!DesignMode)
        {
            gpuUpdateTimer = new System.Threading.Timer(UpdateGpuView, null, 0, 1000);
            cpuUpdateTimer = new System.Threading.Timer(UpdateCpuView, null, 0, 100);
        }
    }

    private string GpuText
    {
        set
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action(() => gpuLabel.Text = value), null);
            }
        }
    }

    private string TemperatureLabel
    {
        set
        {
            if (InvokeRequired)
            {
                BeginInvoke(new Action(() => temperatureLabel.Text = value), null);
            }
        }
    }

    private void UpdateCpuView(object state)
    {
        // do your stuff here
        // 
        // do not access control directly, use BeginInvoke!
        TemperatureLabel = sensor.Value.ToString() + "c" // whatever
    }

    private void UpdateGpuView(object state)
    {
        // do your stuff here
        // 
        // do not access control directly, use BeginInvoke!
        GpuText = sensor.Value.ToString() + "c";  // whatever
    }

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (cpuTimer != null)
            {
                cpuTimer.Dispose();
            }
            if (gpuTimer != null)
            {
                gpuTimer.Dispose();
            }
        }

        base.Dispose(disposing);
    }

You can't just throw this code into a background worker and expect it to work. 您不能只是将此代码丢给后台工作人员并期望它能工作。 Anything that updates UI elements (labels, textboxes, ...) needs to be invoked on the main thread. 任何更新UI元素(标签,文本框等)的操作都需要在主线程上调用。 You need to break out your logic to get the data and the logic to update the UI. 您需要打破逻辑以获取数据,并需要逻辑来更新UI。

I would say your best bet is to do this: 我会说最好的选择是这样做:

In the timer Tick() method: 在计时器Tick()方法中:

// Disable the timer.
// Start the background worker

In the background worker DoWork() method: 在后台工作程序DoWork()方法中:

// Call your functions, taking out any code that 
// updates UI elements and storing this information 
// somewhere you can access it once the thread is done.

In the background worker Completed() method: 在后台工作程序的Completed()方法中:

// Update the UI elements based on your results from the worker thread
// Re-enable the timer.

First make sure to get your head around multithreathing and it's problems (especially UI stuff). 首先,请确保避免遇到麻烦,这是个问题(尤其是UI方面的问题)。

Then you can use somethink like 然后您可以使用类似

public class Program
{
    public static void Main(string[] args)
    {
        Timer myTimer = new Timer(TimerTick, // the callback function
            new object(), // some parameter to pass
            0, // the time to wait before the timer starts it's first tick
            1000); // the tick intervall
    }

    private static void TimerTick(object state)
    {
        // less then .NET 4.0
        Thread newThread = new Thread(CallTheBackgroundFunctions);
        newThread.Start();

        // .NET 4.0 or higher
        Task.Factory.StartNew(CallTheBackgroundFunctions);
    }

    private static void CallTheBackgroundFunctions()
    {
        cpuView();
        gpuView();
    }
}

Please keep in mind (just like John Koerner told you) your cpuView() and gpuView() will not work as is. 请记住(就像John Koerner告诉您的那样),您的cpuView()gpuView()无法按原样工作。

Yes you can: 是的你可以:

In your Timer tick event: 在您的计时器滴答事件中:

private void timer_Tick(object sender, EventArgs e)
{

  timer.Enabled = false;

  backgroundworker.RunWorkerAsync();

  timer.Enabled = true;
}

In your Backgroundworker dowork event: 在您的Backgroundworker dowork事件中:

private void backgroundworker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
   try
   {
       //Write what you want to do
   }
   catch (Exception ex)
   {
       MessageBox.Show("Error:\n\n" + ex.Message, "System", MessageBoxButtons.OK, MessageBoxIcon.Error);
   }
}

I think BackgroundWorker is too complex thing for the case; 我认为BackgroundWorker对于这种情况来说太复杂了; with Timer it is difficult to implement guaranteed stopping. 使用Timer很难实现保证的停止。

I would like to recommend you using worker Thread with the loop which waits cancellation ManualResetEvent for the interval you need: 我建议您将工作Thread与循环配合使用,该循环在您需要的时间间隔内等待取消ManualResetEvent

  • If the cancellation event is set then the worker exits the loop. 如果设置了取消事件,则工作程序退出循环。
  • If there is a timeout (time interval you need exceeds) then perform system monitoring. 如果存在超时(您需要的时间间隔超出),请执行系统监视。

Here is the draft version of the code. 这是代码的草稿版本 Please note I have not tested it , but it could show you the idea. 请注意, 我尚未对其进行测试 ,但是它可以向您展示这个想法。

public class HardwareMonitor
{
    private readonly object _locker = new object();
    private readonly TimeSpan _monitoringInterval;
    private readonly Thread _thread;
    private readonly ManualResetEvent _stoppingEvent = new ManualResetEvent(false);
    private readonly ManualResetEvent _stoppedEvent = new ManualResetEvent(false);

    public HardwareMonitor(TimeSpan monitoringInterval)
    {
        _monitoringInterval = monitoringInterval;
        _thread = new Thread(ThreadFunc)
            {
                IsBackground = true
            };
    }

    public void Start()
    {
        lock (_locker)
        {
            if (!_stoppedEvent.WaitOne(0))
                throw new InvalidOperationException("Already running");

            _stoppingEvent.Reset();
            _stoppedEvent.Reset();
            _thread.Start();
        }
    }

    public void Stop()
    {
        lock (_locker)
        {
            _stoppingEvent.Set();
        }
        _stoppedEvent.WaitOne();
    }

    private void ThreadFunc()
    {
        try
        {
            while (true)
            {
                // Wait for time interval or cancellation event.
                if (_stoppingEvent.WaitOne(_monitoringInterval))
                    break;

                // Monitoring...
                // NOTE: update UI elements using Invoke()/BeginInvoke() if required.
            }
        }
        finally
        {
            _stoppedEvent.Set();
        }
    }
}

In my case I was using a BackgroundWorker ,a System.Timers.Timer and a ProgressBar in WinForm Application. 就我而言,我在WinForm应用程序中使用BackgroundWorkerSystem.Timers.TimerProgressBar What I came across is on second tick that I will repeat the BackgroundWorker's Do-Work I get a Cross-Thread Exception while trying to update ProgressBar in ProgressChanged of BackgroundWorker .Then I found a solution on SO @Rudedog2 https://stackoverflow.com/a/4072298/1218551 which says that When you initialize the Timers.Timer object for use with a Windows Form, you must set the SynchronizingObject property of the timer instance to be the form. 我遇到的是第二个滴答,我将重复BackgroundWorker的工作,尝试更新BackgroundWorker的ProgressChanged中的ProgressBar时遇到跨线程异常 。然后我在SO @ Rudedog2 https://stackoverflow.com上找到了解决方案 / a / 4072298/1218551 ,它表示在初始化要与Windows窗体一起使用的Timers.Timer对象时,必须将计时器实例的SynchronizingObject属性设置为窗体。

systemTimersTimerInstance.SynchronizingObject = this; // this = form instance.

http://msdn.microsoft.com/en-us/magazine/cc164015.aspx http://msdn.microsoft.com/zh-CN/magazine/cc164015.aspx

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM