运行 两个任务同时进行

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 Threads¹. 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));
    }
}