运行 两个任务同时进行
Running two tasks simultaneously
您好,我有一个 winform 应用程序,它由 25 个文本框组成,这些文本框代表实时接收的值,而它们下面有标签显示这些值的最小最大值和平均值flow in shown with red arrows
现在,当应用程序启动时,它会延迟 1 秒在每个文本框及其标签中填充数据。因此它需要 7 到 8 秒才能完全填充数据。
我想要的是将填充文本框和标签的任务分成两部分
我尝试使用它进行多线程处理,但它对我不起作用
到目前为止我的代码
Thread mmathread;
while (start)
{
try
{
RPM_TEXT.Text = ReadRPM().ToString() + " rpm";
mmathread = new Thread(() => mma(rpm_list, ReadRPM(), RPM_mma));
mmathread.Start();
}
catch { }
try { EL_TEXT.Text = ReadEngineLoad().ToString() + " %";
mmathread = new Thread(() => mma(el_list, ReadEngineLoad(), EL_mma));
mmathread.Start();
}
catch { }
try { ECT_TEXT.Text = ReadCoolantTemp().ToString() + " °C";
mmathread = new Thread(() => mma(ect_list, ReadCoolantTemp(), ECT_mma));
mmathread.Start();
}
catch { }
}
函数定义为
private void mma(List<int> parameter_list, int parameter, Label label)
{
if (parameter_list.Count == 9) { parameter_list.Add(parameter);
parameter_list.RemoveAt(0); }
else { parameter_list.Add(parameter); }
minpara = parameter_list.Min();
maxpara = parameter_list.Max();
avrgpara = parameter_list.Sum() / parameter_list.Count;
label.Text = $"Min = {minpara}, Avg = {avrgpara}, Max = {maxpara}";
}
如果我直接调用函数,它会更新标签,但如果我向它添加线程,标签不会填充
使用线程时,您需要在标签上使用调用。像这样
label.Invoke((MethodInvoker) delegate () {label.Text = $"Min = {minpara}, Avg = {avrgpara}, Max = {maxpara}"};
https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke?view=net-5.0
您可以使用 Task<T>
s instead of Thread
s¹. The Task.Run
method offloads the supplied action to the ThreadPool
, and the result of the action is later available through the Result
property of the Task<T>
. You can await
Task<T>
,这样 UI 将在后台 运行 操作时保持响应。您还可以一次启动多个任务,以便并行调用操作。在这种情况下,您可以使用 Task.WhenAll
方法 await
所有并行任务。示例:
while (start)
{
Task delayTask = Task.Delay(TimeSpan.FromSeconds(1));
Task<string> task1 = Task.Run(() => ReadRPM());
Task<string> task2 = Task.Run(() => ReadEngineLoad());
Task<string> task3 = Task.Run(() => ReadCoolantTemp());
await Task.WhenAll(task1, task2, task3);
RPM_TEXT.Text = task1.Result;
EL_TEXT.Text = task2.Result;
ECT_TEXT.Text = task3.Result;
await delayTask;
}
在此示例中,我添加了一个 Task.Delay
任务,以施加每秒一个循环的最大频率。
此代码必须放在具有 async
modifier, to enable the await
运算符的方法(或事件处理程序)中。
¹ 我指的是 Thread
class,而不是 thread as a concept。
详细说明@BojanB 的回答,您可以使用传统的 WinForms InvokeRequired
/Invoke
模式。如果在非 UI 线程上计算 InvokeRequired
属性 returns 则为真。如果需要 Invoke
调用,那么您只需 Invoke
相同的调用(将调用编组到 UI 线程)即可完成。如果您这样做,那么可以从 UI 线程或任何其他线程调用相同的函数。
首先我创建了这三个辅助函数:
private void SetTextBoxFromThread(TextBox textBox, string contents)
{
if (InvokeRequired)
{
Invoke(new Action<TextBox, string>(SetTextBoxFromThread), new object[] { textBox, contents });
return;
}
textBox.Text = contents;
}
private string ReadTextBoxFromThread(TextBox textBox)
{
if (InvokeRequired)
{
return (string)Invoke(new Func<TextBox, string>(ReadTextBoxFromThread), new[] { textBox });
}
return textBox.Text;
}
private void SetLabelFromThread(Label label, string contents)
{
if (InvokeRequired)
{
Invoke(new Action<Label, string>(SetLabelFromThread), new object[] { label, contents });
return;
}
label.Text = contents;
}
有了这些,我通过使用这个愚蠢的小线程函数(匹配 ThreadStart
委托的 async
版本)测试了它们:
private int isPaused = 0; //used like a bool, int for clear atomicity
private async void ThreadFunc()
{
while (isPaused == 0)
{
var now = DateTime.Now;
var str1 = now.ToString("T");
var str2 = now.ToString("t");
var str3 = now.ToString("s");
SetTextBoxFromThread(textBox1, str1);
SetTextBoxFromThread(textBox2, str2);
SetTextBoxFromThread(textBox3, str3);
await Task.Delay(1500);
SetLabelFromThread(label1, ReadTextBoxFromThread(textBox1));
SetLabelFromThread(label2, ReadTextBoxFromThread(textBox2));
SetLabelFromThread(label3, ReadTextBoxFromThread(textBox3));
}
}
您好,我有一个 winform 应用程序,它由 25 个文本框组成,这些文本框代表实时接收的值,而它们下面有标签显示这些值的最小最大值和平均值flow in shown with red arrows
现在,当应用程序启动时,它会延迟 1 秒在每个文本框及其标签中填充数据。因此它需要 7 到 8 秒才能完全填充数据。
我想要的是将填充文本框和标签的任务分成两部分
我尝试使用它进行多线程处理,但它对我不起作用
到目前为止我的代码
Thread mmathread;
while (start)
{
try
{
RPM_TEXT.Text = ReadRPM().ToString() + " rpm";
mmathread = new Thread(() => mma(rpm_list, ReadRPM(), RPM_mma));
mmathread.Start();
}
catch { }
try { EL_TEXT.Text = ReadEngineLoad().ToString() + " %";
mmathread = new Thread(() => mma(el_list, ReadEngineLoad(), EL_mma));
mmathread.Start();
}
catch { }
try { ECT_TEXT.Text = ReadCoolantTemp().ToString() + " °C";
mmathread = new Thread(() => mma(ect_list, ReadCoolantTemp(), ECT_mma));
mmathread.Start();
}
catch { }
}
函数定义为
private void mma(List<int> parameter_list, int parameter, Label label)
{
if (parameter_list.Count == 9) { parameter_list.Add(parameter);
parameter_list.RemoveAt(0); }
else { parameter_list.Add(parameter); }
minpara = parameter_list.Min();
maxpara = parameter_list.Max();
avrgpara = parameter_list.Sum() / parameter_list.Count;
label.Text = $"Min = {minpara}, Avg = {avrgpara}, Max = {maxpara}";
}
如果我直接调用函数,它会更新标签,但如果我向它添加线程,标签不会填充
使用线程时,您需要在标签上使用调用。像这样
label.Invoke((MethodInvoker) delegate () {label.Text = $"Min = {minpara}, Avg = {avrgpara}, Max = {maxpara}"};
https://docs.microsoft.com/en-us/dotnet/api/system.windows.forms.control.invoke?view=net-5.0
您可以使用 Task<T>
s instead of Thread
s¹. The Task.Run
method offloads the supplied action to the ThreadPool
, and the result of the action is later available through the Result
property of the Task<T>
. You can await
Task<T>
,这样 UI 将在后台 运行 操作时保持响应。您还可以一次启动多个任务,以便并行调用操作。在这种情况下,您可以使用 Task.WhenAll
方法 await
所有并行任务。示例:
while (start)
{
Task delayTask = Task.Delay(TimeSpan.FromSeconds(1));
Task<string> task1 = Task.Run(() => ReadRPM());
Task<string> task2 = Task.Run(() => ReadEngineLoad());
Task<string> task3 = Task.Run(() => ReadCoolantTemp());
await Task.WhenAll(task1, task2, task3);
RPM_TEXT.Text = task1.Result;
EL_TEXT.Text = task2.Result;
ECT_TEXT.Text = task3.Result;
await delayTask;
}
在此示例中,我添加了一个 Task.Delay
任务,以施加每秒一个循环的最大频率。
此代码必须放在具有 async
modifier, to enable the await
运算符的方法(或事件处理程序)中。
¹ 我指的是 Thread
class,而不是 thread as a concept。
详细说明@BojanB 的回答,您可以使用传统的 WinForms InvokeRequired
/Invoke
模式。如果在非 UI 线程上计算 InvokeRequired
属性 returns 则为真。如果需要 Invoke
调用,那么您只需 Invoke
相同的调用(将调用编组到 UI 线程)即可完成。如果您这样做,那么可以从 UI 线程或任何其他线程调用相同的函数。
首先我创建了这三个辅助函数:
private void SetTextBoxFromThread(TextBox textBox, string contents)
{
if (InvokeRequired)
{
Invoke(new Action<TextBox, string>(SetTextBoxFromThread), new object[] { textBox, contents });
return;
}
textBox.Text = contents;
}
private string ReadTextBoxFromThread(TextBox textBox)
{
if (InvokeRequired)
{
return (string)Invoke(new Func<TextBox, string>(ReadTextBoxFromThread), new[] { textBox });
}
return textBox.Text;
}
private void SetLabelFromThread(Label label, string contents)
{
if (InvokeRequired)
{
Invoke(new Action<Label, string>(SetLabelFromThread), new object[] { label, contents });
return;
}
label.Text = contents;
}
有了这些,我通过使用这个愚蠢的小线程函数(匹配 ThreadStart
委托的 async
版本)测试了它们:
private int isPaused = 0; //used like a bool, int for clear atomicity
private async void ThreadFunc()
{
while (isPaused == 0)
{
var now = DateTime.Now;
var str1 = now.ToString("T");
var str2 = now.ToString("t");
var str3 = now.ToString("s");
SetTextBoxFromThread(textBox1, str1);
SetTextBoxFromThread(textBox2, str2);
SetTextBoxFromThread(textBox3, str3);
await Task.Delay(1500);
SetLabelFromThread(label1, ReadTextBoxFromThread(textBox1));
SetLabelFromThread(label2, ReadTextBoxFromThread(textBox2));
SetLabelFromThread(label3, ReadTextBoxFromThread(textBox3));
}
}