同步方法
程序运行的时候,在调用其他方法的时候,会等待被调用的方法按顺序执行完,才会继续执行。非常符合开发思维,有序执行(这个意思是干啥都是一起干(不管多少程序)等最慢的或者最长的完成之后才可以统一完成(铁哥们)(括号内自我理解如果不对就别当回事))
异步方法
在程序调用异步方法的时候,主程序不会等待方法执行完,而是主程序调用异步方法后直接继续运行,而异步方法会启动一个新线程来完成方法的计算。(异步相等于VIP干啥都是有特殊通道(括号内自我理解如果不对就别当回事))
异步编程
委托的异步调用
- 先根据比较耗时的功能方法声明对应的委托
- 给委托赋值,赋与这个耗时的功能函数
- 采用委托的BeginInvoke方法进行异步调用委托
Invoke方法:类似于默认的方法调用,相当于直接使用()调用委托
BeginInvoke方法:指采取异步调用委托 - 给BeginInvoke方法创建回调函数
回调函数:回调函数是指,当前的正在执行的操作完成之后,立刻回调用的一个函数是回调函数
BeginInvoke方法参数组成:- 第一组参数:委托所指向的方法对应的参数
- AsyncCallback参数:与当前委托对应的回调函数
- object参数:假如回调函数需要参数则由object参数提供
BeginInvoke方法返回值为IAsyncResult,而委托的回调函数必须有一个参数为IAsyncResult类型
- 通过EndInvoke获取对应委托的BeginInvoke的执行结果
- 因为BeginInvoke相当于重新创建的线程进行异步调用方法函数,所以回调函数也是异步的线程的执行的,那么异步线程和程序主线程相当于阳关道和独木桥,
因此如果使用Winform或WPF中的控件要考虑清楚:UI中的控件等都是Winform或WPF主线程中创建的,而异步线程无法直接使用
设置CheckForIllegalCrossThreadCalls =false,这个属性是用来设置Winform或者WPF中的主线程不在做控件的跨线程检测
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click_1(object sender, EventArgs e)
{
int a = 0;
//多线程编程
//委托:ParameterizedThreadStart
Thread objThread1 = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
a += i;
//当 一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它,此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,
if (label1.InvokeRequired)
{
//Invoke方法:类似于默认的方法调用,相当于直接使用()调用委托
label1.Invoke(new Action<string>(s =>
{
label1.Text = $"【{s}】";
}), a.ToString());
}
Thread.Sleep(500);
}
});
objThread1.IsBackground = true;//设置为后台线程
objThread1.Start();//启动任务,将其调度到当前任务调度程序执行。
}
private void button2_Click(object sender, EventArgs e)
{
int a = 0;
Thread objThread02 = new Thread(() =>
{
for (int i = 0; i < 100; i++)
{
a += i;
if (label2.InvokeRequired)
{
label2.Invoke(new Action<int>(s =>
{
label2.Text = $"【{s}】";
}), a);
Thread.Sleep(200);
}
}
});
objThread02.IsBackground = true;
objThread02.Start();
}
}
异步编程总结
- 异步编程是建立在委托基础上的一种编程方法
- 异步调用的每个方法都是独立的线程执行。因此。本质上就是一种多线程程序,也可以说是一种简化版本的多线程技术
- 比较适合在后台运行较为耗时的简单任务,并且任务要求相互独立,任务中不应该有代码直接访问可视化控件
- 如果后台任务要求必须按照特定顺序执行,或者必须访问公共资源,则异步编程不适合,而直接使用多线程技术
多线程的异步
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click_1(object sender, EventArgs e)
{
int a = 0;
//多线程编程
//委托:ParameterizedThreadStart
Thread objThread1 = new Thread(() =>
{
for (int i = 0; i < 10; i++)
{
a += i;
//当 一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它,此时它将会在内部调用new MethodInvoker(LoadGlobalImage)来完成下面的步骤,
if (label1.InvokeRequired)
{
//Invoke方法:类似于默认的方法调用,相当于直接使用()调用委托
label1.Invoke(new Action<string>(s =>
{
label1.Text = $"【{s}】";
}), a.ToString());
}
Thread.Sleep(500);
}
});
objThread1.IsBackground = true;//设置为后台线程
objThread1.Start();//启动任务,将其调度到当前任务调度程序执行。
}
private void button2_Click(object sender, EventArgs e)
{
int a = 0;
Thread objThread02 = new Thread(() =>
{
for (int i = 0; i < 100; i++)
{
a += i;
if (label2.InvokeRequired)
{
label2.Invoke(new Action<int>(s =>
{
label2.Text = $"【{s}】";
}), a);
Thread.Sleep(200);
}
}
});
objThread02.IsBackground = true;
objThread02.Start();
}
}
后台线程
指的是程序中的一些功能不需要和一般线程同时执行,或者这些功能对于UI界面中显示方面要求不高,可以设置为后台线程
例如:VS工具中的,编译检测功能就是后台线程,当VS启动起来,除过前台线程给我们展示编辑菜单等界面,同时后台运行了检测程序
句柄(Handle)
句柄是一个标识符,用于代表操作系统中的各种资源,比如各种窗体、GDI绘图对象、进程和线程对象、文件等都用句柄
句柄使用
- 即使进程退出,很多时候操作系统仍然保持进程句柄,操作系统句柄有限,使用用需要节省,比如打开文件后,使用完毕要及时关闭
- 句柄可以通过Process对象的Handle属性访问,比如进程退出的代码,时间等
总结:拥有图形界面的程序都有一个主窗体,这个窗体也对应有一个句柄。当窗体关闭时进程也关闭。主窗体一般由主线程负责创建