C#--多线程Thread和线程池ThreadPool
以下是学习笔记:
一、多线程的学习
Thread基础使用-->原理分析-->ThreadPool-->Task(主流)
二、为什么学多线程?
在单核时代,我们写的程序,如果没有多线程,就会经常卡顿,但是并不是说用了多线程程序就绝对不卡顿。因为单核时代,即使你用
多线程,实际上,根本不是并行执行。其实是通过时间片切换,达到“并行”目的。
在多核时代,首先从硬件上,就已经实现了真正的并行执行,但是是不是所有的都能并行执行呢?
三、将同步和异步多线程比较
异步多线程:在一定程度上,能够提升系统的性能。改进用户体验。
【1】同步效果:

【2】异步效果:

【3】同步和异步的代码:
#region 同步任务
private void btnSync1_Click(object sender, EventArgs e)
{
for (int i = 1; i < 20; i++)
{
Console.WriteLine(i);
Thread.Sleep(200);
}
}
private void btnSync2_Click(object sender, EventArgs e)
{
for (int i = 1; i < 20; i++)
{
Console.WriteLine($"-------------------------{i}-----------------------");
Thread.Sleep(400);
}
}
#endregion
#region 异步任务
private void btnExecute1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
for (int i = 1; i < 20; i++)
{
Console.WriteLine(i);
Thread.Sleep(200);
}
});
thread.IsBackground = true;//设置为后台线程(主程序关闭的时候,后台程序自动关闭的)
thread.Start();
}
private void btnExecute2_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
for (int i = 1; i < 20; i++)
{
Console.WriteLine($"-------------------------{i}-----------------------");
Thread.Sleep(400);
}
});
thread.IsBackground = true;
thread.Start();
}
#endregion
四、跨线程访问控件
【1】效果展示

【2】代码:
//通过跨线程给控件赋值
private void btnExecute1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
// this.lblResult1.Text = i.ToString();//报错:线程间操作无效,从不是创建控件lblResult1的线程访问它
//因为上面thread单独创建了一个子线程,lblResult1是在主线程中创建的,子线程不能直接操作。
if (this.lblResult1.InvokeRequired)//this.lblResult1.InvokeRequired判断需不需要跨线程来访问,这个判断可以不加的
{
this.lblResult1.Invoke(
new Action<string>(data => { this.lblResult1.Text = data; }),
i.ToString()
);//参数1:委托,参数2:委托的参数
Thread.Sleep(300);
}
}
});
thread.IsBackground = true;
thread.Start();
}
//通过线程给控件赋值
private void btnExecute2_Click(object sender, EventArgs e)
{
for (int i = 0; i < 10; i++)
{
this.lblResult2.Text = i.ToString();
Thread.Sleep(300);
}
}
//通过跨线程读取控件的值
private void btnRead_Click(object sender, EventArgs e)
{
//传统方法
//this.lblV.Text = this.txtV.Text;
//如果涉及到一些查询延迟计算的,可以用跨线程
Thread thread = new Thread(() =>
{
this.txtV.Invoke(new Action<string>(data =>
{
this.lblV.Text = data;//将读取的控件值,在其他控件lblV中显示出来
}), this.txtV.Text);//读取的输入:txtV
});
thread.IsBackground = true;
thread.Start();
}
【3】数据库访问
//访问数据库
private void btnExecute1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
string classCount = DBUtility.SQLHelper.GetSingleResult("select count(*) from Products").ToString();
this.lblResult1.Invoke(new Action<string>(count => { this.lblResult1.Text = count; }), classCount);
});
thread.IsBackground = true;
thread.Start();
}
private void btnExecute2_Click(object sender, EventArgs e)
{
}
//跨线程访问数据库,并在dgv中展示数据
private void btnGetData_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
DataSet ds = DBUtility.SQLHelper.GetDataSet("select * from ProductInventory;select ProductId,ProductName,Unit from Products");
DataTable dt1 = ds.Tables[0];
DataTable dt2 = ds.Tables[1];
this.dgv1.Invoke(new Action<DataTable>(t => { this.dgv1.DataSource = t; }), dt1);
this.dgv2.Invoke(new Action<DataTable>(t => { this.dgv2.DataSource = t; }), dt2);
});
thread.IsBackground = true;
thread.Start();
}
五、多线程底层观察
六,线程生命周期
【1】演示:

【2】代码
private Thread thread = null;
private int counter = 0;
//【1】开启
private void btnStart_Click(object sender, EventArgs e)
{
thread = new Thread(() =>
{
while (true)
{
try
{
Thread.Sleep(500);
lblInfo.Invoke(new Action(() =>
{
lblInfo.Text += counter++ + ",";
}));
}
catch (Exception ex)
{
MessageBox.Show(ex.Message + " 异常位置:" + counter++);
}
}
});
thread.Start();
}
//暂停(线程挂起),只能暂定正在运行的线程或休眠额线程
private void btnSuspend_Click(object sender, EventArgs e)
{
if (thread.ThreadState == ThreadState.Running ||
thread.ThreadState == ThreadState.WaitSleepJoin)
{
thread.Suspend();
}
}
//继续(继续已挂起的线程)
private void btnResume_Click(object sender, EventArgs e)
{
if (thread.ThreadState == ThreadState.Suspended )
{
thread.Resume();
}
}
//中断
private void btnInterrupt_Click(object sender, EventArgs e)
{
thread.Interrupt();
}
//终止
private void btnAbort_Click(object sender, EventArgs e)
{
thread.Abort();
}
【3】等待子线程执行完成
Thread thread = new Thread(new ThreadStart(() =>
{
Thread.Sleep(3000);
Console.WriteLine("这个是正在执行的子线程数据......");
}));
thread.Start();
thread.Join();//会等待子线程执行完毕后,在执行下面的主线程内容。
Console.WriteLine("这个是主线程的数据...");

七,线程池
当我们开发中,用的线程比较多的时候,就要考虑性能问题。所以,我们可以开几个线程,方便使用。
【1】线程池的基本使用
#region 线程池ThreadPool的基本使用
static void Method11()
{
ThreadPool.QueueUserWorkItem((arg) =>
{
//请在这里编写实际的要处理的内容...
Console.WriteLine("Method11子线程Id:" + Thread.CurrentThread.ManagedThreadId);
});
Console.WriteLine("Method11主线程Id:" + Thread.CurrentThread.ManagedThreadId);
}
//给线程传参数
static void Method12()
{
ThreadPool.QueueUserWorkItem((arg) =>
{
//请在这里编写实际的要处理的内容...
Console.WriteLine("Method12子线程Id:" + Thread.CurrentThread.ManagedThreadId);
Console.WriteLine("arg=" + arg);
}, "arg具体的参数");
Console.WriteLine("Method12主线程Id:" + Thread.CurrentThread.ManagedThreadId);
}
#endregion

【2】线程和ThreadPool和Thread性能比较
#region 线程和ThreadPool和Thread性能比较
static void Method2()
{
for (int i = 1; i <= 10; i++)
{
Thread thread = new Thread(() =>
{
Console.WriteLine($"子线程{i} Id:" + Thread.CurrentThread.ManagedThreadId);
for (int a = 1; a <= 5; a++)
{
Console.WriteLine(a);
}
});
thread.Name = "线程名称:" + i;
thread.IsBackground = true;
thread.Start();
Thread.Sleep(50);
}
}
static void Method3()
{
Console.WriteLine("------------------------使用线程池----------------------------");
for (int i = 1; i <= 10; i++)
{
ThreadPool.QueueUserWorkItem((arg) =>
{
Console.WriteLine($"子线程{i} Id:" + Thread.CurrentThread.ManagedThreadId);
for (int a = 1; a <= 5; a++)
{
Console.WriteLine(a);
}
});
Thread.Sleep(50);
}
}
#endregion
用Thread的结果:
子线程1 Id:3 1 2 3 4 5 子线程2 Id:4 1 2 3 4 5 子线程3 Id:5 1 2 3 4 5 子线程4 Id:6 1 2 3 4 5 子线程5 Id:7 1 2 3 4 5 子线程6 Id:8 1 2 3 4 5 子线程7 Id:9 1 2 3 4 5 子线程8 Id:10 1 2 3 4 5 子线程9 Id:11 1 2 3 4 5 子线程10 Id:12 1 2 3 4 5
用线程池ThreadPool的结果(下面展示的就是开了2个线程,最多的结果开了4个线程)
------------------------使用线程池---------------------------- 子线程1 Id:3 1 2 3 4 5 子线程2 Id:3 1 2 3 4 5 子线程3 Id:4 1 2 3 4 5 子线程4 Id:3 1 2 3 4 5 子线程5 Id:4 1 2 3 4 5 子线程6 Id:3 1 2 3 4 5 子线程7 Id:4 1 2 3 4 5 子线程8 Id:3 1 2 3 4 5 子线程9 Id:4 1 2 3 4 5 子线程10 Id:3 1 2 3 4 5
【3】分析上面使用线程池为什么最多就开了4个线程
因为电脑里是4核的,4核从效率上来讲是最优的。以后开发过程中能使用线程池的就用线程池

【4】任务管理说明:
内核数是4,逻辑处理器是4,那这个CPU就是真4核,如果这里的内核数是2,逻辑处理器是4,那就是假4核了,实际上是双核四线程。
有人会说,这里看到的CPU有几个框就代表几核,其实这是不准确的,这里的框是线程,不是核,如果是一个双核四线程的CPU,这里显示的就是四个框,那不能说这是个四核CPU,如果非要说四核,那也是假四核了。

浙公网安备 33010602011771号