为什么 C# 中的多线程运行很慢
本文关键字:运行 多线程 为什么 | 更新日期: 2023-09-27 18:34:05
当此代码运行并且所有三个线程都在运行时,标签中随机数的显示会变慢。而当停止一个或两个线程时,处理速度会变快。为什么?
namespace MultiThreadingCheckBox
{
public partial class Form1 : Form
{
Thread t1, t2, t3;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
t1 = new Thread(new ThreadStart(DoWork1));
t2 = new Thread(new ThreadStart(DoWork2));
t3 = new Thread(new ThreadStart(DoWork3));
t1.Start();
t2.Start();
t3.Start();
}
private void DoWork1()
{
Random p = new Random();
while (true)
{
label1.Invoke(new MethodInvoker(delegate { label1.Text = p.Next(1, 1000).ToString(); }));
}
}
private void DoWork2()
{
Random p = new Random();
while (true)
{
label2.Invoke(new MethodInvoker(delegate { label2.Text = p.Next(1, 1000).ToString(); }));
}
}
private void DoWork3()
{
Random p = new Random();
while (true)
{
label3.Invoke(new MethodInvoker(delegate { label3.Text = p.Next(1, 1000).ToString(); }));
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (checkBox1.Checked == true)
{
t1.Suspend();
label1.Invoke(new MethodInvoker(delegate { label1.Text = "I am stopped"; }));
}
else
t1.Resume();
}
private void checkBox2_CheckedChanged(object sender, EventArgs e)
{
if (checkBox2.Checked == true)
{
t2.Suspend();
label2.Invoke(new MethodInvoker(delegate { label2.Text = "I am stopped"; }));
}
else
t2.Resume();
}
private void checkBox3_CheckedChanged(object sender, EventArgs e)
{
if (checkBox3.Checked == true)
{
t3.Suspend();
label3.Invoke(new MethodInvoker(delegate { label3.Text = "I am stopped"; }));
}
else
t3.Resume();
}
}
}
首先,在窗体上显示内容需要 UI 线程才能完成工作。
因此,当您执行 Invoke() 时,您会调用对 UI 线程的同步调用来更改显示。
这意味着线程向 UI 线程发送一条消息,并进入睡眠状态,直到收到完成的信号。
若要避免这种情况,请使用 BeginInvoke
,这将向 UI 线程发送异步消息,并且不会暂停线程。
现在第二个问题是你使用的线程多于你拥有的内核。
因此,由于实际的物理原因,CPU无法真正并行运行它,它没有另一个内核可以做到这一点。
所以这不是真正的并行性,除了线程在调用中进入睡眠状态之外,性能会降低。
首先,因为每次线程等待执行时,您的所有线程进程都有继续循环。
现在第二件事是系统配置。如果您有多核系统,那么每个内核可以执行一个线程,因此如果 4 个内核,那么三个线程并行执行,但如果单核或两个内核,则线程必须等待执行。
第三点是,每次使用 invoke for label 将每个开关上下文更新回 UI 线程时,也会降低和影响应用程序的性能。
顺便说一句,由于Yochai Timmer和dotnetstep已经解释了为什么它执行缓慢,我还想建议使用任务而不是生成自己的线程。任务将为您管理线程池,并且可能比您在不投入太多精力的情况下实际管理的效率更高。
显然,您编写的是一个示例应用程序,因此您可能已经了解任务。在这种情况下,我同意Yochai的观点,您绝对应该使用BeginInvoke来异步访问UI。