(精华)2020年8月12日 C#基础知识点 异步专题(发展史,多线程,多进程,协程,单线程非阻塞)
一:引言
/// .NetFramework1.0就有多线程!
/// 进程:计算机概念,程序运行在服务器占据的全部计算机的资源
/// 线程:计算机概念,是进程在相应操作时候的一个最小单元,也包括cpu/硬盘/内存 虚拟概念
/// 进程和线程:包含关系,线程是属于某一个进程的,如果一个进程销毁,线程也就不会存在。
/// 句柄:描述程序中的某一个最小单元,是一个long数字,操作系统通过这个数字识别应用程序。
/// 多线程:计算概念,就是某一个进程中,多个线程同时运行;
///
/// C#中的多线程:
/// Thread类是C#语言对线程对象一个封装;
///
/// 为什么可以多线程呢?
///
/// 1、Cpu有多个核;可以并行计算;
/// 双核四线:这里的线程是模拟核;
///
/// 2、cpu分片:某1s的处理能切分成1000份,操作系统调度去相应不同的任务;
/// 从宏观角度来说:感觉就有多个任务在并发执行;
/// 从微观角度来说:一个物理cpu不能在某一刻为某一个任务服务
///
/// 同步异步:
/// 同步方法:发起调用,只有在调用的方法完成以后,才能继续执行一下一行代码,按照顺序执行;
/// 诚心请吃饭,我请你吃饭,你说你现在需要忙一会儿,我等你,等你忙完了以后,咱们一起去吃饭。
///
/// 异步方法:发起调用,不等待完成,直接进入下一行代码的执行,启动一个新的线程来完成计算
///
/// 客气一下请人吃饭:我请你吃饭,你说你现在需要忙一会儿,我就不等你了,我自己先去吃饭了,你忙完以后,自己去吃饭。
二:同步和异步的比较
#region Sync
/// <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 l = 3;
int m = 4;
int n = l + m;
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 Async
/// <summary>
/// 异步方法:
/// 1、同步方法卡界面:主线线程(UI线程)忙于计算,无暇他顾
/// 异步方法不卡界面:因为异步方法是新启动一个线程去完后计算,主线程闲置
/// 改善用户体验,winform程序点击某一个按钮,不会卡死界面;
/// 发短信,发邮件可以交给一个子线程去完成
///
/// 2、同步方法执行慢:只有一个线程完成计算
/// 异步方法执行快:多个线程去完成计算
/// 10000ms 3000ms 快了三倍多
/// 20000ms 15000ms cpu密集型计算
/// 资源换性能
///
/// 3、同步方法有序执行,异步多线程无顺序
/// 启动无序,线程资源是向操作系统申请的,操作系统有自己的调度策略,所以启动是随机的;
/// 以上两点得出: 结束也是没有顺序
///
/// 如果需要控制顺序呢?怎么实现?
/// </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;
for (int i = 0; i < 5; i++)
{<!-- -->
action.BeginInvoke("btnAsync_Click", null, null);
}
Console.WriteLine($"****************btnAsync_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 Start {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
long lResult = 0;
Thread.Sleep(2000);//线程等待
Console.WriteLine($"****************DoSomethingLong End {name} {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} {lResult}***************");
}
#endregion
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")}***************");
IAsyncResult asyncResult = null;
AsyncCallback callback = ar =>
{<!-- -->
Thread.Sleep(5000);
Console.WriteLine($"这里是beginInvoke的第三个参数{ar.AsyncState}");
Console.WriteLine(object.ReferenceEquals(ar, asyncResult));
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy - MM - dd HH: mm:ss.fff")}");
Console.WriteLine("计算结束");
};
{<!-- -->
//1、回调
Action<string> action = this.DoSomethingLong;
//把自定义的参数传入到回调函数中去
asyncResult = action.BeginInvoke("btnAsyncAdvanced_Click", callback, "八万公里");
//2、IsCompleted 完成等待
{<!-- -->
int i = 0;
while (!asyncResult.IsCompleted)
{<!-- -->
if (i < 9)
{<!-- -->
Console.WriteLine($"正在玩命为你加载中。。。已经完成{++i * 10}%");
}
else
{<!-- -->
Console.WriteLine($"正在玩命为你加载中。。。已经完成99.9999%");
}
Thread.Sleep(200);
}
Console.WriteLine("加载完成。。。");
}
//以上两种都是为了等待任务的完成;
//3、WaitOne等待
asyncResult.AsyncWaitHandle.WaitOne();//一直等待任务完成
asyncResult.AsyncWaitHandle.WaitOne(-1);//一直等待任务完成
asyncResult.AsyncWaitHandle.WaitOne(3000);//最多等待3000ms,如果超时了,就不等待了
{<!-- -->
//4、EndInvoke也可以等待,可以获取委托返回值
Func<int> func = () =>
{<!-- -->
//Thread.Sleep(5000);
return DateTime.Now.Year;
};
func.Invoke();
IAsyncResult asyncResult1 = func.BeginInvoke(ar =>
{<!-- -->
func.EndInvoke(ar);
}, null);
int iResult = func.EndInvoke(asyncResult1);
Console.WriteLine(iResult);
}
Console.WriteLine($"****************btnAsyncAdvanced_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
三:.NetFramework1.0(Thread)
{<!-- -->
ParameterizedThreadStart threadStart = ar =>
{<!-- -->
this.DoSomethingLong("btnThread_Click");
Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
};
Thread thread = new Thread(threadStart);
thread.Start(); //开启一个新线程
}
{<!-- -->
ThreadStart threadStart = () =>
{<!-- -->
Thread.Sleep(5000);
this.DoSomethingLong("btnThread_Click");
Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
};
Thread thread = new Thread(threadStart);
thread.Start(); //开启一个新线程
thread.Suspend();// 暂停线程
thread.Resume();//恢复 无法实时的去暂停或者恢复线程
thread.Abort();//终结线程
Thread.ResetAbort();//都会有延时
//如果我们需要等待;
//1、等待
while (thread.ThreadState != ThreadState.Stopped)
{<!-- -->
Thread.Sleep(200);
}
//2、Join等待
thread.Join();//可以限时等待
thread.Join(2000); //可以限时等待
thread.Priority = ThreadPriority.Highest;//设置线程优先级,只是增加他的优先概率;并不能一定的
thread.IsBackground = true;//为后台线程 进程结束,线程结束了
thread.IsBackground = false; //前台线程 进程结束后,任务执行完毕以后,线程才结束
{<!-- -->
ThreadStart threadStart1 = () =>
{<!-- -->
Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
Thread.Sleep(3000);
};
Action action = () =>
{<!-- -->
Console.WriteLine($"****************btnThread_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
Thread.Sleep(3000);
};
this.ThreadWithCallBack(threadStart1, action);
}
{<!-- -->
//Thread开启一个新的线程执行任务,如何获取返回结果:
Func<int> func = () =>
{<!-- -->
Thread.Sleep(5000);
return DateTime.Now.Year;
};
Func<int> FuncResult = this.ThreadWithReturn(func);
int iResult = FuncResult.Invoke();//如果需要得到执行结果,是必须要等待的
}
}
/// <summary>
/// 开启线程执行委托函数
/// </summary>
/// <param name="threadStart"></param>
/// <param name="actionCallback"></param>
private void ThreadWithCallBack(ThreadStart threadStart, Action actionCallback)
{<!-- -->
ThreadStart threadStart1 = new ThreadStart(() =>
{<!-- -->
threadStart.Invoke();
actionCallback.Invoke();
});
Thread thread = new Thread(threadStart1);
thread.Start();
}
/// <summary>
/// 既要不卡界面,又需要返回结果
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="func"></param>
/// <returns></returns>
private Func<T> ThreadWithReturn<T>(Func<T> func)
{<!-- -->
T t = default(T);
ThreadStart threadStart = new ThreadStart(() =>
{<!-- -->
t = func.Invoke();
});
Thread thread = new Thread(threadStart);
thread.Start();
return new Func<T>(() =>
{<!-- -->
thread.Join();
return t;
});
}
四:.NetFramework2.0(ThreadPool)
/// <summary>
/// 线程池
/// .NetFramework2.0
/// 在Thread中对线程的管理需要我们自己去从操作,在不断的开启线程和销毁中,存在很大的开销,为了让线程可以反复的使用,出现了池化思想!
/// 可以节省资源,线程池还可以控制线程总数量,防止滥用!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThreadPool_Click(object sender, EventArgs e)
{<!-- -->
Console.WriteLine($"****************btnThreadPool_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
{<!-- -->
ThreadPool.QueueUserWorkItem(o =>
{<!-- -->
Console.WriteLine($"**************** {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
this.DoSomethingLong("ThreadPool.QueueUserWorkItem1");//开启了一个线程
});
}
{<!-- -->
ThreadPool.QueueUserWorkItem(o =>
{<!-- -->
Console.WriteLine($"第二个参数:{o}");
this.DoSomethingLong("ThreadPool.QueueUserWorkItem1");//开启了一个线程
}, "Hyl");
}
{<!-- -->
ThreadPool.GetMaxThreads(out int maxWorkerThreads, out int maxCompletionPortThreads);
ThreadPool.GetMinThreads(out int minWorkerThreads, out int minCompletionPortThreads);
Console.WriteLine($"当前电脑最大workerThreads={maxWorkerThreads},最大的completionPortThreads={maxCompletionPortThreads}");
Console.WriteLine($"当前电脑最小workerThreads={minWorkerThreads},最小的completionPortThreads={minCompletionPortThreads}");
Console.WriteLine("设置线程数量之后:");
//设置线程数量是全局,线程池是全局,Task,async/awit 都是来自于线程,老师不建议大家随便设置!
ThreadPool.SetMaxThreads(2, 2); //这里在设置的时候,数量不能低于本计算机的 核数
ThreadPool.SetMinThreads(2, 2);
ThreadPool.GetMaxThreads(out int maxWorkerThreads1, out int maxCompletionPortThreads1);
ThreadPool.GetMinThreads(out int minWorkerThreads1, out int minCompletionPortThreads1);
Console.WriteLine($"当前电脑最大workerThreads={maxWorkerThreads1},最大的completionPortThreads={maxCompletionPortThreads1}");
Console.WriteLine($"当前电脑最小workerThreads={minWorkerThreads1},最小的completionPortThreads={minCompletionPortThreads1}");
}
{<!-- -->
//线程等待:
//在线程池中,有一个开关式
ManualResetEvent manualResetEvent = new ManualResetEvent(false); //开关关闭
ThreadPool.QueueUserWorkItem(o =>
{<!-- -->
this.DoSomethingLong("btnThreadPool_Click1");
Thread.Sleep(3000);
manualResetEvent.Set();// 开关打开
});
manualResetEvent.WaitOne();//执行到这儿来的时候,我就等你给我发信号
Console.WriteLine("计算完成");
}
{<!-- -->
ThreadPool.SetMaxThreads(8, 8);//设置了最大的线程数量
ManualResetEvent manualResetEvent = new ManualResetEvent(false); //开关关闭
for (int i = 0; i < 10; i++)
{<!-- -->
int k = i;
ThreadPool.QueueUserWorkItem(t =>
{<!-- -->
Console.WriteLine($"{Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
if (k == 9)
{<!-- -->
manualResetEvent.Set();
}
else
{<!-- -->
manualResetEvent.WaitOne();
}
});
}
if (manualResetEvent.WaitOne())
{<!-- -->
Console.WriteLine("执行成功!");
}
}
Console.WriteLine($"****************btnThreadPool_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
五:.NetFramework3.0(Task)
/// <summary>
/// Task 是.NetFramework3.0出现的。提供了非常多的Api
/// Task里面的线程是来自于线程池!
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btntask_Click(object sender, EventArgs e)
{<!-- -->
Console.WriteLine($"****************btnThreadPool_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}***************");
{<!-- -->
Task task = new Task(() =>
{<!-- -->
this.DoSomethingLong("btntask_Click1");
});
task.Start();//开启了一个新的线程
}
{<!-- -->
Task.Run(() =>
{<!-- -->
this.DoSomethingLong("btntask_Click2");
});
}
{<!-- -->
//TaskFactory taskFactory = new TaskFactory();
TaskFactory taskFactory = Task.Factory;
taskFactory.StartNew(() =>
{<!-- -->
this.DoSomethingLong("btntask_Click3");
});
}
{<!-- -->
Thread thread = new Thread(() => {<!-- --> Console.WriteLine("开启了一个新线程"); });
thread.Start();
ThreadPool.QueueUserWorkItem(o => {<!-- --> Console.WriteLine("开启了一个新线程"); });
}
{<!-- -->
{<!-- -->
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Thread.Sleep(2000);
stopwatch.Stop();
Console.WriteLine($"{stopwatch.ElapsedMilliseconds}");
}
{<!-- -->
// Task.Delay 出现于4.5版本
Stopwatch stopwatch = new Stopwatch();
stopwatch.Start();
Task task = Task.Delay(2000).ContinueWith(t => //任务在2000ms 以后执行
{<!-- -->
stopwatch.Stop();
Console.WriteLine($"{stopwatch.ElapsedMilliseconds}");
Console.WriteLine("回调已完成");
});
}
}
{<!-- -->
//可以并发执行的时候就可以使用多线程
List<Task> tasksList = new List<Task>();
TaskFactory taskFactory = new TaskFactory();
tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("001", "系统管理"); }));
tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("002", "部门管理"); }));
tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("003", "客户管理"); }));
tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("004", "接口管理"); }));
tasksList.Add(taskFactory.StartNew(() => {<!-- --> this.Coding("005", "写Api"); }));
//如果有一个同学完成了某一个模块,老师就需要准备环境!
// 等待某一个线程执行完毕以后 继续往后执行
Task.WaitAny(tasksList.ToArray());
Task.WaitAll(tasksList.ToArray()); //阻塞主线程
}
Console.WriteLine($"****************btnThreadPool_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
六:.NetFramework4.5(Parallel )
/// <summary>
/// Parallel 对Task进一步进行了封装 .Netframework 4.5版本出来
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnParallel_Click(object sender, EventArgs e)
{<!-- -->
Console.WriteLine($"**************** btnParallel_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
{<!-- -->
//多线程中控制执行顺序,一直是一个难题,使用Parallel可以控制线程的执行顺序。
//Parallel并发执行了五个委托,开启了新线程,主线程参与计算,界面会阻塞
// Task WaitAll + 主线程
Parallel.Invoke(() => {<!-- --> this.DoSomethingLong("btnParallel_Click_1"); },
() => {<!-- --> this.DoSomethingLong("btnParallel_Click_2"); },
() => {<!-- --> this.DoSomethingLong("btnParallel_Click_3"); },
() => {<!-- --> this.DoSomethingLong("btnParallel_Click_4"); },
() => {<!-- --> this.DoSomethingLong("btnParallel_Click_5"); });
}
{<!-- -->
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 2;//最大并行数
Parallel.For(0, 10, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
}
{<!-- -->
控制线程数量
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 2; //控制线程的最大数量
//控制执行数量
Parallel.ForEach(new int[] {<!-- --> 12, 13, 14, 15, 16, 17 }, parallelOptions, t => this.DoSomethingLong($"btnParallel_Click_{t}"));
}
Console.WriteLine($"**************** btnParallel_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
七:多线程异常处理和异常取消
/// <summary>
/// 多线程异常处理
/// 线程取消
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void btnThreadCore_Click(object sender, EventArgs e)
{<!-- -->
Console.WriteLine($"**************** btnThreadCore_Click Start {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
//多线程中如果某一个线程异常了,就会终结当前线程;对其他的线程是没有影响的;
//多线程中的异常去哪儿了?
//被吞掉了
#region 多线程异常处理
{<!-- -->
Thread thread = new Thread('执行方法');
thread.Abort();
try
{<!-- -->
List<Task> taskList = new List<Task>();
for (int i = 0; i < 100; i++)
{<!-- -->
string name = $"btnThreadCore_Click_{i}";
int k = i;
taskList.Add(Task.Run(() =>
{<!-- -->
if (k == 5)
{<!-- -->
throw new Exception($"{name} 异常了");
}
else if (k == 6)
{<!-- -->
throw new Exception($"{name} 异常了");
}
else if (k == 10)
{<!-- -->
throw new Exception($"{name} 异常了");
}
Console.WriteLine($"this is {name} Ok!");
}));
};
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException aex) //可以有多个Catch 在匹配异常类型的时候,先具体,然后在寻找父类
{<!-- -->
foreach (var exception in aex.InnerExceptions)
{<!-- -->
Console.WriteLine(exception.Message);
}
}
catch (Exception ex)
{<!-- -->
Console.WriteLine(ex.Message);
throw;
}
//在实际的工作中,往往有很多场景是如果发生异常之后,其他的线程就需要取消,不再继续往下执行;问题就是如何取消线程;
}
#endregion
#region 线程取消
{<!-- -->
//全局变量
//bool IsOk = true;
CancellationTokenSource cts = new CancellationTokenSource();// 通知式的
try
{<!-- -->
List<Task> taskList = new List<Task>();
for (int i = 0; i < 100; i++)
{<!-- -->
string name = $"btnThreadCore_Click_{i}";
int k = i;
taskList.Add(Task.Run(() =>
{<!-- -->
if (k == 5)
{<!-- -->
throw new Exception($"{name} 异常了");
}
if (!cts.IsCancellationRequested)//是否取消
{<!-- -->
Console.WriteLine($"this is {name} Ok!");
}
else
{<!-- -->
Console.WriteLine($"this is {name} Stop!");
}
}));
};
Task.WaitAll(taskList.ToArray());
}
catch (AggregateException aex) //可以有多个Catch 在匹配异常类型的时候,先具体,然后在寻找父类
{<!-- -->
cts.Cancel(); //执行该方法以后,IsCancellationRequested会被指定为false
foreach (var exception in aex.InnerExceptions)
{<!-- -->
Console.WriteLine(exception.Message);
}
}
catch (Exception ex)
{<!-- -->
Console.WriteLine(ex.Message);
throw;
}
}
#endregion
Console.WriteLine($"**************** btnThreadCore_Click End {Thread.CurrentThread.ManagedThreadId.ToString("00")} {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")} ***************");
}
八:多线程安全问题
private static readonly object Obj_Lock = new object();
//1 Lock;不能锁null,不建议大家锁String; 锁 this
//使用锁:private static readonly object Obj_Lock = new object()
//锁的作用:排他
//2 线程安全集合
// System.Collections.Concurrent.ConcurrentStack 基于线程安全
//3 数据分拆,避免多个线程操作同一堆数据,安全又高效率
//保证线程安全不会使用同一个变量
for (int i = 0; i < 100000; i++)
{<!-- -->
this.NumOne += 1;
}
for (int i = 0; i < 100000; i++)
{<!-- -->
Task.Run(() =>
{<!-- -->
try
{<!-- -->
lock (Obj_Lock )//可以 避免多线程并发,如果锁住以后,其实这里跟单线程 基本上没啥区别;
{<!-- -->
this.NumTow += 1;
}
}
catch (Exception)
{<!-- -->
throw;
}
});
}
/// <summary>
/// new Random().Next(1, 100); 多线程同时执行结果很高概率相同,
/// 是用的当前时间为seed,时间相同结果相同
///
/// 解决随机数重复问题
/// 同时模拟远程请求的随机延时
/// </summary>
public class RandomHelper
{<!-- -->
/// <summary>
/// 随机获取数字并等待1~2s
/// </summary>
/// <returns></returns>
public int GetRandomNumberDelay(int min, int max)
{<!-- -->
Thread.Sleep(this.GetRandomNumber(500, 1000));//随机休息一下
return this.GetRandomNumber(min, max);
}
/// <summary>
/// 获取随机数,解决重复问题
/// </summary>
/// <param name="min"></param>
/// <param name="max"></param>
/// <returns></returns>
public int GetRandomNumber(int min, int max)
{<!-- -->
Guid guid = Guid.NewGuid();//每次都是全新的ID
string sGuid = guid.ToString();
int seed = DateTime.Now.Millisecond;
for (int i = 0; i < sGuid.Length; i++)
{<!-- -->
switch (sGuid[i])
{<!-- -->
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case 'g':
seed = seed + 1;
break;
case 'h':
case 'i':
case 'j':
case 'k':
case 'l':
case 'm':
case 'n':
seed = seed + 2;
break;
case 'o':
case 'p':
case 'q':
case 'r':
case 's':
case 't':
seed = seed + 3;
break;
case 'u':
case 'v':
case 'w':
case 'x':
case 'y':
case 'z':
seed = seed + 3;
break;
default:
seed = seed + 4;
break;
}
}
Random random = new Random(seed);
return random.Next(min, max);
}
}
九:最终版(async/await)
public class AwaitAsyncClass
{<!-- -->
public async static void TestShow()
{<!-- -->
Test();
}
private async static Task Test()
{<!-- -->
Console.WriteLine($"当前主线程id={Thread.CurrentThread.ManagedThreadId.ToString("00")}");
{<!-- -->
Task t = NoReturnTask();
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
t.Wait();//主线程等待Task的完成 阻塞的
await t;//await后的代码会由线程池的线程执行 非阻塞
}
{<!-- -->
Task<long> t = SumAsync();
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
long lResult = t.Result;//访问result 主线程等待Task的完成
t.Wait();//等价于上一行
}
{<!-- -->
Task<int> t = SumFactory();
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
long lResult = t.Result;//没有await和async 普通的task
t.Wait();
}
Console.WriteLine($"Main Thread Task ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Console.Read();
}
/// <summary>
/// 无返回值 async Task == async void
/// Task和Task<T>能够使用await, Task.WhenAny, Task.WhenAll等方式组合使用。Async Void 不行
/// </summary>
/// <returns></returns>
private static async Task NoReturnTask()
{<!-- -->
//这里还是主线程的id
Console.WriteLine($"NoReturnTask Sleep before await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
Task task = Task.Run(() =>
{<!-- -->
Console.WriteLine($"NoReturnTask Sleep3000 before,ThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(3000);
Console.WriteLine($"NoReturnTask Sleep3000 after,ThreadId={Thread.CurrentThread.ManagedThreadId}");
});
await task;
Console.WriteLine($"NoReturnTask Sleep after await,ThreadId={Thread.CurrentThread.ManagedThreadId}");
}
/// <summary>
/// 带返回值的Task
/// 要使用返回值就一定要等子线程计算完毕
/// </summary>
/// <returns>async 就只返回long</returns>
private static async Task<long> SumAsync()
{<!-- -->
Console.WriteLine($"SumAsync 111 start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
long result = 0;
await Task.Run(() =>
{<!-- -->
for (int k = 0; k < 10; k++)
{<!-- -->
Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
for (long i = 0; i < 999_999_999; i++)
{<!-- -->
result += i;
}
});
Console.WriteLine($"SumFactory 111 end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
await Task.Run(() =>
{<!-- -->
for (int k = 0; k < 10; k++)
{<!-- -->
Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
for (long i = 0; i < 999999999; i++)
{<!-- -->
result += i;
}
});
Console.WriteLine($"SumFactory 111 end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
await Task.Run(() =>
{<!-- -->
for (int k = 0; k < 10; k++)
{<!-- -->
Console.WriteLine($"SumAsync {k} await Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
Thread.Sleep(1000);
}
for (long i = 0; i < 999999999; i++)
{<!-- -->
result += i;
}
});
Console.WriteLine($"SumFactory 111 end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
return result;
}
/// <summary>
/// 真的返回Task 不是async
///
/// 要使用返回值就一定要等子线程计算完毕
/// </summary>
/// <returns>没有async Task</returns>
private static Task<int> SumFactory()
{<!-- -->
Console.WriteLine($"SumFactory 111 start ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
TaskFactory taskFactory = new TaskFactory();
Task<int> iResult = taskFactory.StartNew<int>(() =>
{<!-- -->
Thread.Sleep(3000);
Console.WriteLine($"SumFactory 123 Task.Run ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
return 123;
});
//Console.WriteLine($"This is {iResult.Result}");
Console.WriteLine($"SumFactory 111 end ManagedThreadId={Thread.CurrentThread.ManagedThreadId}");
return iResult;
}
}