带库的 C# BackgroundWorker
C# BackgroundWorker with Library
我已经集成了 EasyModBus 库,现在想使用后台工作程序通过 ModBus 每 250 毫秒查询一次值。消息 modbusclient is null 出现在后台工作程序中。如何在后台工作程序中获取 modbusclient 功能?有什么方法可以添加功能吗?
private void backgroundworker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
label10.Text = modbusclient.ReadInputRegisters(4, 1)[0].ToString() + " kHz"; //read register 300005 for frequency
label11.Text = modbusclient.ReadInputRegisters(5, 1)[0].ToString() + " W"; //read register 300006 for power
label12.Text = modbusclient.ReadInputRegisters(6, 1)[0].ToString() + " %"; //read register 300007 for amplitude in %
TimeSpan t = TimeSpan.FromMilliseconds(Convert.ToDouble(modbusclient.ReadInputRegisters(8, 1)[0].ToString()));
string runningtime = string.Format("{0:D2}m:{1:D2}s",
t.Minutes,
t.Seconds);
label14.Text = runningtime;
}
我的建议是放弃 anachronistic BackgroundWorker
class, and use Task.Run
and async/await。首先创建一个异步方法,每 250 毫秒连续循环一次并更新标签:
private async Task InfiniteLoopAsync(CancellationToken cancellationToken = default)
{
while (true)
{
var delayTask = Task.Delay(250, cancellationToken);
var value10 = await Task.Run(() => modbusclient.ReadInputRegisters(4, 1)[0]);
var value11 = await Task.Run(() => modbusclient.ReadInputRegisters(5, 1)[0]);
var value12 = await Task.Run(() => modbusclient.ReadInputRegisters(6, 1)[0]);
var value14 = await Task.Run(() => modbusclient.ReadInputRegisters(8, 1)[0]);
label10.Text = value10.ToString() + " kHz";
label11.Text = value11.ToString() + " W";
label12.Text = value12.ToString() + " %";
TimeSpan t = TimeSpan.FromMilliseconds(Convert.ToDouble(value14));
label14.Text = $"{t.Minutes:D2}m:{t.Seconds:D2}s";
await delayTask;
}
}
然后在表单首次显示时启动异步循环,最终在表单即将关闭时停止循环:
private CancellationTokenSource _cts = new();
private Task _infiniteLoop = Task.CompletedTask;
private void Form_Shown(object sender, EventArgs e)
{
_infiniteLoopTask = InfiniteLoopAsync(_cts.Token);
}
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
_cts.Cancel();
// Wait the completion of the loop before closing the form
try { _infiniteLoopTask.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore this error
}
重要的是,与 UI 控件的任何交互都只发生在 UI 线程上。仅当此代码既不读取也不更新 UI 组件时,您才能将代码卸载到 ThreadPool
。使用 await Task.Run
,您将获得卸载操作的结果,然后您回到 UI 线程,更新标签是合法的。
通过你的问题,我注意到winform平台的存在。
我的建议是您使用 Timer 实现。
它有点类似于您在上面尝试实现的 BackgroundWorker。不同之处在于,您将 Interval
属性 的值设置为 250
,以便在 250 毫秒后 event Tick
中的代码将被触发为 运行。
此外,您还需要在每个事件执行之前添加条件if (modbusclient == null) return;
行,这有助于您避免异常。
并且在开始 Timer.start()
之前不要忘记检查 modbusclient
是否已初始化!
参考:https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=netframework-4.7.2
我已经集成了 EasyModBus 库,现在想使用后台工作程序通过 ModBus 每 250 毫秒查询一次值。消息 modbusclient is null 出现在后台工作程序中。如何在后台工作程序中获取 modbusclient 功能?有什么方法可以添加功能吗?
private void backgroundworker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
label10.Text = modbusclient.ReadInputRegisters(4, 1)[0].ToString() + " kHz"; //read register 300005 for frequency
label11.Text = modbusclient.ReadInputRegisters(5, 1)[0].ToString() + " W"; //read register 300006 for power
label12.Text = modbusclient.ReadInputRegisters(6, 1)[0].ToString() + " %"; //read register 300007 for amplitude in %
TimeSpan t = TimeSpan.FromMilliseconds(Convert.ToDouble(modbusclient.ReadInputRegisters(8, 1)[0].ToString()));
string runningtime = string.Format("{0:D2}m:{1:D2}s",
t.Minutes,
t.Seconds);
label14.Text = runningtime;
}
我的建议是放弃 anachronistic BackgroundWorker
class, and use Task.Run
and async/await。首先创建一个异步方法,每 250 毫秒连续循环一次并更新标签:
private async Task InfiniteLoopAsync(CancellationToken cancellationToken = default)
{
while (true)
{
var delayTask = Task.Delay(250, cancellationToken);
var value10 = await Task.Run(() => modbusclient.ReadInputRegisters(4, 1)[0]);
var value11 = await Task.Run(() => modbusclient.ReadInputRegisters(5, 1)[0]);
var value12 = await Task.Run(() => modbusclient.ReadInputRegisters(6, 1)[0]);
var value14 = await Task.Run(() => modbusclient.ReadInputRegisters(8, 1)[0]);
label10.Text = value10.ToString() + " kHz";
label11.Text = value11.ToString() + " W";
label12.Text = value12.ToString() + " %";
TimeSpan t = TimeSpan.FromMilliseconds(Convert.ToDouble(value14));
label14.Text = $"{t.Minutes:D2}m:{t.Seconds:D2}s";
await delayTask;
}
}
然后在表单首次显示时启动异步循环,最终在表单即将关闭时停止循环:
private CancellationTokenSource _cts = new();
private Task _infiniteLoop = Task.CompletedTask;
private void Form_Shown(object sender, EventArgs e)
{
_infiniteLoopTask = InfiniteLoopAsync(_cts.Token);
}
private void Form_FormClosing(object sender, FormClosingEventArgs e)
{
_cts.Cancel();
// Wait the completion of the loop before closing the form
try { _infiniteLoopTask.GetAwaiter().GetResult(); }
catch (OperationCanceledException) { } // Ignore this error
}
重要的是,与 UI 控件的任何交互都只发生在 UI 线程上。仅当此代码既不读取也不更新 UI 组件时,您才能将代码卸载到 ThreadPool
。使用 await Task.Run
,您将获得卸载操作的结果,然后您回到 UI 线程,更新标签是合法的。
通过你的问题,我注意到winform平台的存在。
我的建议是您使用 Timer 实现。
它有点类似于您在上面尝试实现的 BackgroundWorker。不同之处在于,您将 Interval
属性 的值设置为 250
,以便在 250 毫秒后 event Tick
中的代码将被触发为 运行。
此外,您还需要在每个事件执行之前添加条件if (modbusclient == null) return;
行,这有助于您避免异常。
并且在开始 Timer.start()
之前不要忘记检查 modbusclient
是否已初始化!
参考:https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.timer?view=netframework-4.7.2