8异步和多线程

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了8异步和多线程相关的知识,希望对你有一定的参考价值。

1,.NET 中实现异步的方式是使用委托的beginInvoke方法。

使用异步之后.net会创建一个线程来异步执行方法操作,主线程会继续执行程序逻辑。如果在异步执行中有回调函数,在异步方法执行完之后执行异步调用的线程回再执行回调函数中的代码。

1,首先创建一个比较耗时的私有方法用以作为异步执行的代码,后续的多线程执行代码使用的也是该代码。

 1 /// <summary>
 2         /// 一个比较耗时耗资源的私有方法
 3         /// </summary>
 4         /// <param name="name"></param>
 5         private void DoSomethingLong(string name)
 6         {
 7             Console.WriteLine($"****************DoSomethingLong Start {name} {System.Threading.Thread.CurrentThread.ManagedThreadId}***************");
 8             long lResult = 0;
 9             for (int i = 0; i < 1000000000; i++)
10             {
11                 lResult += i;
12             }
13             //Thread.Sleep(2000);
14 
15             Console.WriteLine($"****************DoSomethingLong   End  {name} {System.Threading.Thread.CurrentThread.ManagedThreadId}***************");
16         }

然后声明一个委托,用私有方法实例化,调用代码如下:

 1 Action<string> act = this.DoSomethingLong;
 2 
 3                 AsyncCallback callback = t =>
 4                 {
 5                     Console.WriteLine("t的值为:" + t);
 6                     Console.WriteLine($"执行callback回调函数的线程是{System.Threading.Thread.CurrentThread.ManagedThreadId}");
 7                 };
 8                 IAsyncResult asyncResult = act.BeginInvoke("btnasync_Click", callback, "标识参数");
 9 
10                 Console.WriteLine("异步之外的函数执行1");
11                 Console.WriteLine("异步之外的函数执行2");
12                 Console.WriteLine("异步之外的函数执行3");
13                 act.EndInvoke(asyncResult);

执行效果:技术分享

执行效果可以看出,异步线程执行私有的耗时方法,主线程直接执行下面的打印方法,在异步方法执行完毕之后子线程又执行了回调函数。

2,使用Thread方式执行多线程

 1  private Func<T> ThreadWithReturn<T>(Func<T> func) {
 2             T t = default(T);
 3             ThreadStart threadStart = () => {
 4                 func.Invoke();
 5             };
 6             System.Threading.Thread thread = new System.Threading.Thread(threadStart);
 7             thread.Start();
 8             return new Func<T>(() =>{
 9                 thread.Join(); //让当前线程加入到主线程中
10                 return t;
11             });
12         } 

调用:

1  Func<int> funcInvoke = this.ThreadWithReturn<int>(func);
2             Console.WriteLine("执行其他的操作1");
3             System.Threading.Thread.Sleep(1000);
4             Console.WriteLine("执行其他的操作2");
5             System.Threading.Thread.Sleep(1000);
6             Console.WriteLine("执行其他的操作3");
7             Console.WriteLine(funcInvoke.Invoke()); 

3,ThreadPool线程池方式执行多线程

ThreadPool.QueueUserWorkItem(t=>{});   开辟一条线程执行方法函数

实例化ManualResetEvent 类,通过实例化对象调用Set() ,Reset()  ,waiteOne() 方法等待ManualResetEvent对象变成true时才会执行下面的方法。

 1  private void ThreadPool_Click(object sender, EventArgs e)
 2         {
 3             //线程池
 4             {
 5                 Console.WriteLine($"-------线程池执行开始------ {System.Threading.Thread.CurrentThread.ManagedThreadId}");
 6                 ManualResetEvent mre = new ManualResetEvent(false); //设置线程池运行信号量,传递false代表关闭状态
 7                 ThreadPool.QueueUserWorkItem(t =>
 8                 {
 9                     Console.WriteLine("t的值是:"+t);
10                     this.DoSomethingLong("ThreadPool_Click");
11                     Console.WriteLine("子线程的线程ID是:" + System.Threading.Thread.CurrentThread.ManagedThreadId);
12                     mre.Set(); //打开
13                                //mre.Reset();//关闭
14                 });
15                 mre.WaitOne(); //等待所有线程执行完毕再执行下面的逻辑,会卡住页面线程
16             }
17 
18             //ThreadPool.SetMaxThreads(8, 8); //设置最大工作线程数量和IO线程数量。
19             //ThreadPool.SetMinThreads(8, 8);
20             #region ManualResetEvent
21             {
22                 ManualResetEvent mre = new ManualResetEvent(false);  //初始化关闭
23                 System.Threading.ThreadPool.QueueUserWorkItem(t =>
24                 {
25                     System.Threading.Thread.Sleep(2000);
26                     Console.WriteLine("第一次执行线程暂停2000ms");
27                     mre.Set();//打开
28                 });
29                 mre.WaitOne(); 
30                 mre.Reset();//关闭
31                 new Action(() =>
32                 {
33                     System.Threading.Thread.Sleep(2000);
34                     Console.WriteLine("第二次执行线程暂停2000ms");
35                     mre.Set();
36                 }).BeginInvoke(null, null);
37                 mre.WaitOne();
38             }
39             #endregion
40             Console.WriteLine($"-------线程池执行结束------ {System.Threading.Thread.CurrentThread.ManagedThreadId}");
41         }

 

4,Task方式执行多线程

Task.Factory.StartNew(() => { this.DoSomethingLong(name); });   开辟一条新的执行线程

ContinueWhenAll和ContinueWhenAny都是异步方式执行的调用,都不会卡主线程。

Task.Factory.ContinueWhenAll(taskList.ToArray(),tList=>{doCallBack。。。});//所有任务执行完成之后再调用回调函数。

Task.Factory.ContinueWhenAny(taskList.ToArray(),t=>{doCallBack。。。});//任意一个任务执行完成之后再调用回调函数。

WaiteAll和WaiteAny都是同步方式执行的调用,都会卡住主线程。

Task.WaiteAll(taskList.ToArray()); //所有任务完成后再执行后面的代码,会卡主线程

Task.WaiteAny(taskList.ToArray()); //任意任务完成后再执行后面的代码,会卡主线程

 1 private void Task_Click(object sender, EventArgs e)
 2         {
 3             Console.WriteLine();
 4             Console.WriteLine();
 5             Console.WriteLine($"------------Task 执行开始 当前线程是:{System.Threading.Thread.CurrentThread.ManagedThreadId}----------"); 
 6             List<Task> taskList = new List<Task>();
 7             for (int i = 0; i < 5; i++)
 8             {
 9                 string name = string.Format("Task_Click"+i); 
10                Task taskNew =  Task.Factory.StartNew(() => {
11                     this.DoSomethingLong(name);
12                 });
13                 taskList.Add(taskNew); 
14             }
15             //等待所有任务都完成再调用回调方法,不会卡主线程
16             //Task.Factory.ContinueWhenAll(taskList.ToArray(), tList =>
17             //{
18             //    Console.WriteLine($"所有任务都执行完毕后调用的回调方法{System.Threading.Thread.CurrentThread.ManagedThreadId}");
19             //});
20 
21             //任意一个任务执行完成就调用,不会卡主线程
22             //Task.Factory.ContinueWhenAny(taskList.ToArray(), t =>
23             //{
24             //    Console.WriteLine(t);
25             //    Console.WriteLine($"任意一个任务执行完毕后调用的回调方法{System.Threading.Thread.CurrentThread.ManagedThreadId}");
26             //});
27 
28              Task.WaitAll(taskList.ToArray());   //等待所有任务完成会卡主线程
29             // Task.WaitAny(taskList.ToArray());   //等待任意一个任务完成
30 
31             Console.WriteLine($"------------Task 执行结束 当前线程是:{System.Threading.Thread.CurrentThread.ManagedThreadId}----------");
32         }

5,异常处理,线程取消,多线程的临时变量和线程安全 lock

 

  1 /// <summary>
  2         /// 异常处理,线程取消,多线程的临时变量和线程安全 lock
  3         /// </summary>
  4         /// <param name="sender"></param>
  5         /// <param name="e"></param>
  6         private void ThreadCore_Click(object sender, EventArgs e)
  7         {
  8             List<Task> taskList = new List<Task>();
  9             
 10             try
 11             {
 12                 #region 线程中的异常处理
 13                 //for (int i = 0; i < 20; i++)
 14                 //{
 15                 //    string name = string.Format($"ThreadCore_Click_{i}");
 16                 //    Action<object> act = t =>
 17                 //    {
 18                 //        try
 19                 //        {
 20                 //            System.Threading.Thread.Sleep(1000);
 21                 //            if (t.Equals("ThreadCore_Click_11"))
 22                 //            {
 23                 //                throw new Exception("ThreadCore_Click_11_Exception");
 24                 //            }
 25                 //            if (t.Equals("ThreadCore_Click_12"))
 26                 //            {
 27                 //                throw new Exception("ThreadCore_Click_12_Exception");
 28                 //            }
 29                 //            Console.WriteLine($"{name} ,执行成功!");
 30                 //        }
 31                 //        catch (Exception ex)
 32                 //        {
 33                 //            //在线程Action加try catch,记录日志,不抛异常。
 34                 //        }
 35                 //    };
 36                 //    taskList.Add(Task.Factory.StartNew(act, name));
 37                 //}
 38                 //Task.WaitAll(taskList.ToArray());  //加上WaitAll
 39                 #endregion
 40 
 41                 #region 线程取消
 42                 // 线程间通信都是通过共有变量:都能访问局部变量/全局变量,数据库值,硬盘文件
 43                 //CancellationTokenSource cts = new CancellationTokenSource();  //线程是否取消,用信号量标记
 44 
 45                 //for (int i = 0; i < 40; i++)
 46                 //{
 47                 //    string name = string.Format($"ThreadCore_Click{i}");
 48                 //    Action<object> act = t =>
 49                 //    {
 50                 //        try
 51                 //        {
 52                 //            System.Threading.Thread.Sleep(2000);
 53                 //            Console.WriteLine($"当前线程为:{System.Threading.Thread.CurrentThread.ManagedThreadId}");
 54                 //            if (t.Equals("ThreadCore_Click11"))
 55                 //            {
 56                 //                throw new Exception("ThreadCore_Click11 执行失败");
 57                 //            }
 58                 //            if (t.Equals("ThreadCore_Click12"))
 59                 //            {
 60                 //                throw new Exception("ThreadCore_Click12 执行失败");
 61                 //            }
 62                 //            if (cts.IsCancellationRequested) //查看当前线程是否已经被取消
 63                 //            {
 64                 //                Console.WriteLine("取消执行" + t);
 65                 //            }
 66                 //            else
 67                 //            {
 68                 //                Console.WriteLine("执行成功" + t);
 69                 //            }
 70                 //        }
 71                 //        catch (Exception ex)
 72                 //        {
 73                 //            cts.Cancel();//出现异常设置未执行的线程取消。
 74                 //            throw;
 75                 //        }
 76                 //    };
 77                 //    taskList.Add(Task.Factory.StartNew(act, name, cts.Token));  //将 cts.Token 传递到执行线程中用以判断
 78                 //}
 79                 //Task.WaitAll(taskList.ToArray());   //必须添加
 80                 #endregion
 81 
 82                 #region 多线程临时变量
 83                 //for (int i = 0; i < 5; i++)
 84                 //{
 85                 //    int k = i;
 86                 //    new Action(()=> {
 87                 //        System.Threading.Thread.Sleep(100);
 88                 //        //Console.WriteLine(i);
 89                 //        Console.WriteLine(k);
 90                 //    }).BeginInvoke(null,null);
 91                 //}
 92                 #endregion
 93 
 94                 #region 线程安全
 95                 for (int i = 0; i < 10000; i++)
 96                 {
 97                     int newI = i;
 98                    taskList.Add(Task.Factory.StartNew(() =>
 99                     {
100                         lock (threadLock) //lock后面的方法块任意时刻只有一个线程进来,此时就变成了单线程。
101                         {
102                             IntTotal += 1;
103                         }
104                     }));
105                 }
106                 Task.WaitAll(taskList.ToArray());
107                 Console.WriteLine($"IntTotal:{IntTotal}");
108                 IntTotal = 0;
109                 #endregion
110             }
111             catch (AggregateException ex)
112             {
113                 foreach (var item in ex.InnerExceptions)
114                 {
115                     Console.WriteLine($"异常信息为:" + item.Message);
116                 }
117             }
118             catch (Exception ex)
119             {
120                 throw;
121             }
122         }

 

以上是关于8异步和多线程的主要内容,如果未能解决你的问题,请参考以下文章

异步和多线程的关系

异步和多线程的关系

编写高质量代码C#建议71:区分异步和多线程应用场景

python 多进程和多线程3 —— asyncio - 异步IO

python 多进程和多线程3 —— asyncio - 异步IO

异步和多线程