C# 线程学习--Async
-
什么是进程?
当一个程序开始运行时,它就是一个进程,进程包括运行中的程序和程序所使用到的内存和系统资源。
而一个进程又是由多个线程所组成的。 -
什么是线程?
线程是程序中的一个执行流,每个线程都有自己的专有寄存器(栈指针、程序计数器等),但代码区是共享的,即不同的线程可以执行同样的函数。 -
什么是多线程?
多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。 -
多线程的好处:
可以提高CPU的利用率。在多线程程序中,一个线程必须等待的时候,CPU可以运行其它的线程而不是等待,这样就大大提高了程序的效率。 -
多线程的不利方面:
线程也是程序,所以线程需要占用内存,线程越多占用内存也越多;
多线程需要协调和管理,所以需要CPU时间跟踪线程;
线程之间对共享资源的访问会相互影响,必须解决竞用共享资源的问题;
线程太多会导致控制太复杂,最终可能造成很多Bug;
进程 线程 多线程 计算机概念
进程:一个程序运行时,占用的全部计算资源的总和
线程:程序执行流的最小单位;任何操作都是由线程完成的;
线程是依托于进程存在的,一个进程可以包含多个线程;
线程也可以有自己的计算资源
多线程:多个执行流同时运行
1 CPU太快了,分时间片--上下文切换(加载环境--计算--保存环境)
微观角度,一个核同一时刻只能执行一个线程;宏观的来说是多线程并发
2 多CPU多核 可以独立工作
4核8线程--核是物理的核 线程是指虚拟核
-
同步
#region ASync
/// <summary>
/// 同步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnSync_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnSync_Click Start
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
int j = 3;
int k = 5;
int m = j + k;
for (int i = 0; i < 5; i++)
{
string name = string.Format($"btnSync_Click_{i}");
this.DoSomethingLong(name);
}
Console.WriteLine($"****************btnSync_Click End
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#endregion
#region Private Method
/// <summary>
/// 一个比较耗时耗资源的私有方法
/// </summary>
/// <param name="name"></param>
private void DoSomethingLong(string name)
{
Console.WriteLine($"****************DoSomethingLong {name} Start
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
for (int i = 0; i < 1000000000; i++)
{
lResult += i;
}
//Thread.Sleep(2000);
Console.WriteLine($"****************DoSomethingLong {name} End
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
-
异步
/// <summary>
/// 异步方法
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsync_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnAsync_Click Start
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
Action<string> action = this.DoSomethingLong;
action.Invoke("btnAsync_Click_1"); // 同步方法 会等待
action("btnAsync_Click_2");
action.BeginInvoke("btnAsync_Click_3",null,null);//发起调用不等待 非阻塞,委托的异步调用
Console.WriteLine($"****************btnAsync_Click End
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
-
Thread是C#语言对线程对象的封装,是对方法执行的描述
同步:完成计算之后,再进入下一行
异步:不会等待方法的完成,会直接进入下一行 非阻塞 -
C# 异步和多线程有什么差别
多线程就是多个thread并发;
异步是硬件式的异步
异步多线程--thread pool task
修改异步方法:
private void btnAsync_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnAsync_Click Start
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
Action<string> action = this.DoSomethingLong;
for (int i = 0; i < 5; i++)
{
//Thread.Sleep(5);
string name = string.Format($"btnAsync_Click_{i}");
//action.BeginInvoke(name, r =>
//{
// //action.EndInvoke(r);//主动调用EndInvoke 可以让线程更好的重用
//}, null);
action.BeginInvoke(name, null, null);
}
Console.WriteLine($"****************btnAsync_Click End
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#endregion
-
同步方法卡界面,主(UI)线程忙于计算;
异步多线程方法不卡界面,主线程完事儿了,计算任务交给子线程在做;
winform提升用户体验;web一个业务操作后要发邮件,异步发送邮件 -
同步方法慢,只有一个线程干活;
异步多线程方法快,因为多个线程并发运算;
并不是线性增长,a资源换时间,可能资源不够 b 多线程也有管理成本
但线程并不是越多越好
多个独立任务可以同时运行; -
异步多线程无序:启动无序 执行时间不确定 结束也无序
一定不要通过等几毫秒的形式来控制启动/执行时间/结束
-
异步回调
#region AsyncAdvanced
/// <summary>
///
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnAsyncAdvanced_Click(object sender, EventArgs e)
{
Console.WriteLine($"****************btnAsyncAdvanced_Click Start
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
Action<string> action = this.DoSomethingLong;
IAsyncResult asyncResult = null;
AsyncCallback callback = ia =>
{
Console.WriteLine(object.ReferenceEquals(asyncResult,ia));
Console.WriteLine(ia.AsyncState);
Console.WriteLine($"到这里计算完成了。{Thread.CurrentThread.ManagedThreadId}");
};
asyncResult =action.BeginInvoke("btnAsyncAdvanced_Click", callback, "hao");
Console.WriteLine($"全部计算真的都完成了,然后给用户返回
{Thread.CurrentThread.ManagedThreadId.ToString("00")}");
Console.WriteLine($"****************btnAsyncAdvanced_Click End
{Thread.CurrentThread.ManagedThreadId.ToString("00")}
{DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
}
#endregion
-
异步调用之后再进行后续操作,通过异步回调来控制顺序
BeginInvoke先完成委托线程方法,执行完之后,把结果作为callBack的参数传进去执行回调,第三个参数就是一个信息 -
异步的等待
-
文件上传
int i = 0;
while (!asyncResult.IsCompleted) // 卡界面,主线程忙于等待
{ // 可以等待,边等待边做其他操作
// 可能最多200ms的延迟
if (i < 10)
{
Console.WriteLine($"文件上传完成{i++ * 10}");
}
else
{
Console.WriteLine($"文件上传完成99.9%...");
}
Thread.Sleep(200);
}
Console.WriteLine($"上传成功了...");
- 通过信号等待
asyncResult.AsyncWaitHandle.WaitOne(); // 等待任务的完成
asyncResult.AsyncWaitHandle.WaitOne(-1);
asyncResult.AsyncWaitHandle.WaitOne(1000);// 限时等待:最多等1000ms
- EndInvoke等待
action.EndInvoke(asyncResult); // 可以等待 可以获取返回值
{
Func<int> func = () =>
{
Thread.Sleep(2000);
return DateTime.Now.Day;
};
Console.WriteLine($"func.Invoke()={func.Invoke()}");
IAsyncResult asyncResult = func.BeginInvoke(r =>
{
// func.EndInvoke(r); //也可以写在里面
Console.WriteLine(r.AsyncState);
}, "hello");
Console.WriteLine($"func.EndInvoke(asyncResult)={func.EndInvoke(asyncResult)}");
}
本文来自博客园,作者:一纸年华,转载请注明原文链接:https://www.cnblogs.com/nullcodeworld/p/18210661