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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/11811048/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-09 19:21:30  来源:igfitidea点击:

How can i use a BackgroundWorker with a timer tick?

c#multithreadingtimer

提问by user1544479

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. In the Shown event im starting the backgroundworker async.

添加了后台工作人员。添加了 Shown 事件,Shown 事件在所有构造函数加载后触发。在 Shown 事件中,我正在启动后台工作器异步。

In the backgroundworker DoWork im doing:

在后台工作人员 DoWork 中,我正在做:

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

采纳答案by Leonardo

In this case it's better to use two System.Threading.Timerand execute your cpu-intensive operations in these two threads. Please note that you mustaccess controls with BeginInvoke. You can encapsulate those accesses into properties setter or even better pull them out to a view model class.

在这种情况下,最好使用两个System.Threading.Timer并在这两个线程中执行 CPU 密集型操作。请注意,您必须使用 访问控件BeginInvoke。您可以将这些访问封装到属性设置器中,甚至更好地将它们拉出到视图模型类中。

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);
    }

回答by John Koerner

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. You need to break out your logic to get the data and the logic to update the UI.

你不能只是把这段代码扔给后台工作人员并期望它工作。任何更新 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.

回答by GameScripting

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 Koernertold you) your cpuView()and gpuView()will not work as is.

请记住(就像John Koerner告诉您的那样)您的cpuView()gpuView()不会按原样工作。

回答by Sergey Brunov

I think BackgroundWorkeris too complex thing for the case; with Timerit is difficult to implement guaranteed stopping.

我认为BackgroundWorker这件事太复杂了;与Timer它是很难实现保证停止。

I would like to recommend you using worker Threadwith the loop which waits cancellation ManualResetEventfor the interval you need:

我想建议您使用Thread带有循环的worker ,该循环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 versionof 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();
        }
    }
}

回答by Omar Chavez

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);
   }
}

回答by katmanco

In my case I was using a BackgroundWorker,a System.Timers.Timerand a ProgressBarin WinFormApplication. What I came across is on second tick that I will repeat the BackgroundWorker's Do-Work I get a Cross-Thread Exceptionwhile trying to update ProgressBar in ProgressChanged of BackgroundWorker .Then I found a solutionon SO @Rudedog2 https://stackoverflow.com/a/4072298/1218551which 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.

就我而言,我在WinForm应用程序中使用了一个BackgroundWorker、一个System.Timers.Timer和一个ProgressBar。我遇到的是在第二个滴答声中,我将重复 BackgroundWorker 的 Do-Work 我在尝试更新 BackgroundWorker 的 ProgressChanged 中的 ProgressBar 时遇到跨线程异常。然后我在 SO @Rudedog2 https://stackoverflow.com上找到了解决方案/a/4072298/1218551表示当您初始化 Timers.Timer 对象以用于 Windows 窗体时,您必须将计时器实例的 SynchronizingObject 属性设置为窗体。

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

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

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