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
  1. 同步方法卡界面,主(UI)线程忙于计算;
    异步多线程方法不卡界面,主线程完事儿了,计算任务交给子线程在做;
    winform提升用户体验;web一个业务操作后要发邮件,异步发送邮件

  2. 同步方法慢,只有一个线程干活;
    异步多线程方法快,因为多个线程并发运算;
    并不是线性增长,a资源换时间,可能资源不够 b 多线程也有管理成本
    但线程并不是越多越好
    多个独立任务可以同时运行;

  3. 异步多线程无序:启动无序 执行时间不确定 结束也无序
    一定不要通过等几毫秒的形式来控制启动/执行时间/结束

  • 异步回调

#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)}");
}
posted @ 2020-05-13 23:12  一纸年华  阅读(9)  评论(0编辑  收藏  举报  来源