尝试解决核心亲和性问题会导致 UI 在重 CPU 负载下停滞

Trying to solve a core affinity problem results in UI stalling under heavy CPU load

我几乎没有线程方面的经验,所以请多多包涵。 我正在制作一个 monitoring/testing 工具,它监视硬件传感器并使用亲和掩码和 for 循环来一个接一个地循环遍历内核 运行 进行全负载单核压力测试。 问题是,当用户开始测试并设置亲和力时,它不是仅将测试方法分配给该核心,而是分配整个程序,这意味着 UI 只能 运行当前经过测试且处于满载状态的核心。 我想很明显 UI 只是在测试过程中停顿,输出监控数据的标签被冻结,而在测试过程中有相关读数是至关重要的,因为这是这个程序的主要目的。

经过一些研究,我认为我需要使用线程,但我以前从未使用过它。 这是我的缩短代码。问题是,当我在任何包含标签的方法上使用线程时,它会抛出错误“跨线程操作无效:控制 'CPUTempTDie' 从创建它的线程以外的线程访问”。我试图在新线程中启动 Report sensors,但没有帮助。我真的试图在一个新线程中启动涉及的每个方法和标签,但它要么没有帮助,要么控制分数(分数 - 是 TestMethod 返回的结果,用于将其与正确的数字进行比较以确认稳定性)或程序只是跳过部分代码,只说“完成”。

问题是:是否可以只设置一个TestingMethod到一个特定的核心,允许程序的其余部分(包括UI)使用任何其他空闲核心,如果没有,我究竟应该在新线程中启动什么才能让 UI 在负载下更新?

//the method below updates labels and calls ReportSensors method that reads
//sensors on a timer tick

private void Monitoring()
{  
    sensor.ReportSensors(); //calls Method that reads sensors

    //Two labels below are stalling when TestingMethod runs

    CPUTempTDie.Value = (int)sensor.CpuTemp;
    FrequencyLabel.Text = sensor.CoreFrequency.ToString("0") + "MHz";  
}

private int TestingMethod()
{
    while (true)
    {
        //Performs calculations to generate load, returns the "score"
    }

    if (timer.Elapsed.TotalSeconds > 60)
    {
        break;
    }

    return score;
}


private async void PerCoreTest()
{
    try
    {
        await Task.Delay(3000);

        for (int i = 0; i < (numberOfCores); i++)
        {
            coreCounter++;

            Thread.BeginThreadAffinity();
            SetThreadAffinityMask(GetCurrentThread(), new IntPtr(intptrVal));

 //TestingMethod below being called twice, and results from both runs
            //are later compared for consistency. 

            TestingMethod();
            iter1 = score / 10000;

            TestingMethod();
            iter2 = score / 10000;


            maxScore = Math.Max(iter1, iter2);

            await Task.Delay(1000);

            TestLabel.Text = score.ToString();

            //Switches to the next thread mask
        }
    }
    finally
    {
        Thread.EndThreadAffinity();
    }
}



private void TestButton_Click(object sender, EventArgs e)
{
    using (Process p = Process.GetCurrentProcess())
        p.PriorityClass = ProcessPriorityClass.High;

    PerCoreTest();

    using (Process p = Process.GetCurrentProcess())
        p.PriorityClass = ProcessPriorityClass.Normal;
}

澄清:尽管 linked thread 没有回答我的问题,但我的问题已作为重复问题关闭。我要求重新打开它,因为:

  1. 虽然链接线程中提到的“大量远程调用大约 2000 - 3000 次调用”在某些硬件上可能很重,但这与计算 CPU 不同在 while(true) 循环中,如果 UI 位于同一个核心上,它会从任何类型的硬件中榨取所有性能,因为 UI 什么都没有。

  2. 据称我重复的线程中的建议解决方案无法解决问题,我原来的问题完全不同:我无法弄清楚必须在任务中放置什么才能使 UI 运行 负载平稳

  3. 我帖子下评论的建议也没有回答这个问题。我尝试了解决方案 Panagiotis Kanavos(见下文)但问题仍然存在:

while (true)
{
    await Task.Delay(500);
    await Task.Run(() => sesnor.ReportSensors()); 
}

研究类似主题后,似乎 none 解决了我的特定问题。

您正在为 UI 线程设置 CPU 亲和力,然后 运行 在同一线程上设置测试例程,因此您的 UI 是有意义的考试的时候挂了。在您开始实际执行测试例程之前,简化事情并确保您的 UI/threading 正常工作。

private int TestingMethod()
{
    // set affinity for current thread here when ready

    // mock a busy thread by sleeping
    System.Threading.Thread.Sleep( 15 * 1000 );

    return 42;
}

// don't use `async void`
private async Task PerCoreTest()
{
    TestLabel.Text = "Running...";

    // we're in the UI thread, so we want to run
    // the test in another thread.  run a new
    // task to do so, await result so the continuation
    // will execute back in the UI thread
    var score = await Task.Run(() => TestingMethod());

    TestLabel.Text = score.ToString();
}

private async Task TestButton_Click(object sender, EventArgs e)
{
    await PerCoreTest();
}

漂亮又简单。在表格中添加其他东西,每秒钟左右更新一次,或者添加一个按钮,您可以单击以验证 UI 是否正确更新,因为测试例程是 运行ning.

一旦您确认 UI 没有锁定,您就可以开始向您的测试例程添加内容。我建议首先获得一个没有处理器亲和力的工作测试例程。

private int TestingMethod()
{
    var score = 0;

    // set affinity for current thread here when ready

    do
    {
        // your cpu-frying logic
    }
    while( /* sentinel condition */ )

    return score;
}

再次验证 UI 在测试期间是否有响应,您还可以验证您的一个核心是否被滥用。一旦验证了所有这些,您就可以设置线程亲和性 INSIDE TestingMethod() 方法的实现(将其抽象为另一个方法调用很好,只要它是从内部调用的TestingMethod 的主体,而不是 运行 在 Task 或其他线程中。您可以将掩码作为参数从 PerCoreTest 传递到 TestingMethod方法。

这应该会让您走上正确的轨道,去做您想做的事。我建议您花一些时间阅读有关多线程的一般知识和 .NET 的 threading/asynchronous 编程模型,如果您打算在未来继续使用它的话。