字面理解:多个线程同时工作的过程。
案例① 单线程
#region ① 单线程做菜
/// <summary>
/// ① 单线程做菜:执行任务时,什么操作都动不了.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void SingleThread_Click(object sender, EventArgs e)
{
Thread.Sleep(3000);
MessageBox.Show("素菜做好了","友情提示");
Thread.Sleep(3000);
MessageBox.Show("荤菜做好了", "友情提示");
}
#endregion
单线程_效果
做菜(先做完素菜,再做荤菜)时,当前窗口无法操作(移动、拉伸等)。
案例② 多线程
#region ② 多线程做菜
/// <summary>
/// ② 多线程做菜:执行任务时,可以对其他功能操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void multiThreading_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
Thread.Sleep(3000);
MessageBox.Show("素菜做好了", "友情提示");
Thread.Sleep(5000);
MessageBox.Show("荤菜做好了", "友情提示");
});
thread.Start();
}
#endregion
多线程_效果
做菜(先做完素菜,再做荤菜)时,当前窗口进可以行操作(移动、拉伸等)。
案例③ 任务
#region ③ 任务做菜
/// <summary>
/// ③ 任务做菜:执行任务时,可以对其他功能操作
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnTask_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
Thread.Sleep(3000);
MessageBox.Show("素菜做好了", "友情提示");
Thread.Sleep(5000);
MessageBox.Show("荤菜做好了", "友情提示");
});
}
#endregion
任务_效果
做菜(先做完素菜,再做荤菜)时,当前窗口进可以行操作(移动、拉伸等)。
案例④ 同步
#region ④ 同步做菜
/// <summary>
/// ④ 同步做菜:同时运行任务
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSynchornization_Click(object sender, EventArgs e)
{
Task.Run(() =>
{
Thread.Sleep(3000);
MessageBox.Show("素菜做好了", "友情提示");
});
Task.Run(() =>
{
Thread.Sleep(5000);
MessageBox.Show("荤菜做好了", "友情提示");
});
}
#endregion
同步_效果
素菜和荤菜可以同时做,当前窗口进可以行操作(移动、拉伸等)。
案例⑤ 异步
#region 做好菜了,来吃饭(异步)
/// <summary>
/// ⑤ 做好菜了,来吃饭(异步):等待任务完成后,才继续往下执行.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private async void btnAsync_Click(object sender, EventArgs e)
{
await Task.Run(() =>
{
Thread.Sleep(3000);
MessageBox.Show("素菜做好了", "友情提示");
Thread.Sleep(5000);
MessageBox.Show("荤菜做好了", "友情提示");
});
MessageBox.Show("做好菜了,来吃饭", "友情提示");
}
#endregion
异步_效果
等待完成这个任务完成后,才执行“来吃饭”;UI和逻辑不在同个线程。
案例⑥ 并行
#region ⑥ 做好菜了,来吃饭(并行)
/// <summary>
/// ⑥ 做好菜了,来吃饭(并行):异步完成任务后,才继续往下执行.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnParallel_Click(object sender, EventArgs e)
{
//1. 创建泛型为Task的集合对象
List<Task> Ltasks = new List<Task>();
//2. 添加任务
Ltasks.Add(Task.Run(() =>
{
Thread.Sleep(3000);
MessageBox.Show("素菜做好了", "友情提示");
}));
Ltasks.Add(Task.Run(() =>
{
Thread.Sleep(5000);
MessageBox.Show("荤菜做好了", "友情提示");
}));
//3. 将所有Task对象完成后,创建新的异步延续任务
Task.WhenAll(Ltasks).ContinueWith(ts =>
{
MessageBox.Show("做好菜了,来吃饭", "友情提示");
});
}
#endregion
并行_效果
等待完成所有任务完成后,才执行“来吃饭”;UI和逻辑不在同个线程。
案例⑦ 使用线程组件跨线程传输信息和状态
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace MyWorkBook.MyTest
{
public partial class ThreadForm : Form
{
public ThreadForm()
{
InitializeComponent();
backgroundWorker1.WorkerReportsProgress = true;//是否支持发送进度报告。
backgroundWorker1.WorkerSupportsCancellation = true;//是否支持取消异步操作。
}
/// <summary>
/// 线程控件触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//通过Object对象可以获取线程组件;以下代码会另起一个线程,而不是在UI线程中运行。
BackgroundWorker worker = sender as BackgroundWorker;
for(int i = 1; i <= 100; i++)
{
if(worker.CancellationPending == true)//应用程序已请求取消后台操作
{
e.Cancel = true;//取消事件--更改运行返回状态,并不能取消停止
break;
}
else
{
Thread.Sleep(50);//实际应用编码时,该程式是为工作代码。
worker.ReportProgress(i);//当线程进度发给改变时,触发线程组件发生改变后触发事件(backgroundWorker1_ProgressChanged)并传参。
/*发送*/
var data = Tuple.Create("Hallen",true,27);//创建元数组数据。
worker.ReportProgress(i, data);
}
}
e.Result = e.Argument;//根据操作异步参数给操作异步结果赋值---应用于线程完成(失败/成功)后触发事件。
}
/// <summary>
/// 线程组件发生改变后触发事件---在创建BackgroundWorker线程上执行,是异步调用,和引发事件代码是在不同线程。
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
progressBar1.Value = e.ProgressPercentage;//将异步任务进度百分比赋值给进度条
resultLabel.Text = (e.ProgressPercentage.ToString() + "%");//将异步任务进度百分比赋值给标签组件显示进度数值
/*接收---跨线程接收信息和状态*/
if(e.UserState != null)
{
Tuple<string, bool, int> data = (Tuple<string, bool, int>)e.UserState;
lbl_state.Text = $"姓名{data.Item1},性别{data.Item2},年龄{data.Item3}";
}
}
/// <summary>
/// 启动按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button1_Click(object sender, EventArgs e)
{
if(backgroundWorker1.IsBusy != true)//判断异步操作不在进行
{
backgroundWorker1.RunWorkerAsync("你是坏淫吗?对,你是的。");//开始异步操作
}
}
/// <summary>
/// 取消按钮触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
if(backgroundWorker1.WorkerSupportsCancellation == true)//判断支持异步取消操作
{
backgroundWorker1.CancelAsync();//请求取消挂起后台操作
}
}
/// <summary>
/// 线程完成(失败/成功)后触发事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if(e.Cancelled == true)//后台操作已取消
{
resultLabel.Text = "Canceled";
}else if(e.Error != null)//异步操作期间发生错误
{
resultLabel.Text = "Error:"+ e.Error.Message;
}
else//后台操作成功
{
resultLabel.Text = "Done! Result = "+ e.Result;
}
}
}
}
运行效果
案例⑧ 使用Invoke跨线程传输信息和状态
#region ②串口数据接收事件
/// <summary>
/// ②串口数据接收事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void port_DataReceived(object sender, SerialDataReceivedEventArgs e)//串口数据接收事件
{
Invoke((EventHandler)delegate //在UI线程上执行委托,而以下代码是委托需求。
{
Thread.Sleep(20);
if (radioButton4.Checked)//接收模式为字符模式
{
string str1 = serialPort1.ReadExisting();//字符串模式读
textBox1.AppendText(str1 + "\r\n");
}//添加内容
if (radioButton2.Checked)//接收模式为数值模式
{
byte data = (byte)serialPort1.ReadByte();//此处需要强制类型转换,将int类型数据转换byte类型数据,不必考虑是否丢失数据
string str2 = Convert.ToString(data, 16).ToUpper();//转换为大写十六进制字符串
textBox1.AppendText("0x" + (str2.Length == 1 ? "0" + str2 : str2) + "\r\n");
}
if (radioButton6.Checked)//接收模式为中文模式
{
//在c#中,如果是通过串口要给下位机发送中文编码或给其他软件(如串口调试助手),则最好采用unicode编码
//System.Text.UnicodeEcoding unic= new System.Text.UnicodeEcoding ();
//Byte[] writeBytes = unic.GetBytes("你好世界");---接收
//serialPort.Write(writeBytes, 0, writeBytes.Length);--发送
UTF8Encoding utf8 = new UTF8Encoding();//创建UTF8编码对象
Byte[] readBytes = new Byte[serialPort1.BytesToRead];//获取缓冲区数据的字节数
serialPort1.Read(readBytes, 0, readBytes.Length);
String decodedString = utf8.GetString(readBytes);//UTF8解码后转换为string类型
textBox1.AppendText(decodedString + "\r\n");
}
});
}
#endregion